///////////////////////////////////////////////////////////////////////////////
//
// lighthouse3D - 3D Software Library
//
// File: C3DLibrary.cpp
//
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2003-2013 Marcus Reinhardt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
///////////////////////////////////////////////////////////////////////////////
//
// You can find a copy of the GNU General Public License (gpl.txt) in the
// directory of the library.
//
///////////////////////////////////////////////////////////////////////////////
#include "C3DLibrary.h"

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::C3DLibrary( ... )
///////////////////////////////////////////////////////////////////////////////
C3DLibrary::C3DLibrary()
{
#ifdef __MULTI_CORE_SUPPORT
  int i;
#endif

  screenSizeX = 1024;
  screenSizeY = 768;
  screenCenterX = (int)(float(screenSizeX) / 2);
  screenCenterY = (int)(float(screenSizeY) / 2);
  opticalCorrectness = 512;
  renderedTrianglesCount = 0;

  screen2D.setSize(1024, 768);

  libSetup.bfCulling = _noBackFaceCulling_;
  libSetup.fsAA = _noFSAntiAliasing_;
  libSetup.texFilter = _noTexFilter_;
  libSetup.texLighting = _lambertLighting_;
  libSetup.texRenderer = _texRenderer_;
  libSetup.texShader = _noTexShader_;
  libSetup.wireFrameColor = 255 + 256*255 + 65536*255;
  libSetup.usedScreen = _useInternal2DScreen_;
  libSetup.fogSynthesis = _noFogSynthesis_;

  lightingHandler.setSpecularPower(7);

  strcpy(libInfo.engineName, "lighthouse3D - 3D Software Library");
  strcpy(libInfo.engineDeveloper, "Marcus Reinhardt");
  strcpy(libInfo.engineVersion, _lib3DVersion_);
  strcpy(libInfo.engineAbilities, 
         libInfo.engineName);
  strcat(libInfo.engineAbilities, 
         " - Version ");
  strcat(libInfo.engineAbilities, 
         libInfo.engineVersion);
  strcat(libInfo.engineAbilities, 
         " - ");
  strcat(libInfo.engineAbilities, 
         "Developed 2003-2013 by ");
  strcat(libInfo.engineAbilities, 
         libInfo.engineDeveloper);
  strcat(libInfo.engineAbilities,
         "\n");
  strcat(libInfo.engineAbilities,
         "\n");
  strcat(libInfo.engineAbilities,
         "Abilities:\n");
  strcat(libInfo.engineAbilities,
         "\n");
  strcat(libInfo.engineAbilities,
         "[01] Geometric Basics like Rotation, Translation, Skalation\n");
  strcat(libInfo.engineAbilities,
         "[02] Hidden Surface Removal (HSR) via Z-Buffer (in combination with Painter's Algorithm) and Backface Culling\n");
  strcat(libInfo.engineAbilities,
         "[03] Perspective Correct Texture Mapping\n");
  strcat(libInfo.engineAbilities,
         "[04] Texture Filtering (Linear, Bilinear)\n");
  strcat(libInfo.engineAbilities,
         "[05] Full Screen Anti Aliasing (FSAA)\n");
  strcat(libInfo.engineAbilities,
         "[06] Calculation of both the Plane Normals and the Vertex Normals\n");
  strcat(libInfo.engineAbilities,
         "[07] Texture Shading (Lambert, Gouraud & Phong) using multiple Point Light Sources\n");
  strcat(libInfo.engineAbilities,
         "[08] Texture Lighting (Lambert & Phong)\n");
  strcat(libInfo.engineAbilities,
         "[09] Camera Implementation providing 6 Degrees of Freedom, Clipping and Collision Detection\n");
  strcat(libInfo.engineAbilities,
		 "[10] Object factory (Plane, Pyramid, Cube, Cylinder, Cone, Sphere and Surface derived from from Heightmap & Bitmap ))\n");
  strcat(libInfo.engineAbilities,
         "[11] Distance Fog Synthesis (Linear, Exponential)\n");
  strcat(libInfo.engineAbilities,
         "[12] Importable 3D File Formats : 3DS (including Textures), OBJ (only model)\n");
  strcat(libInfo.engineAbilities,
         "[13] Importable 2D File Formats : BMP (24-Bit), TGA(24-Bit, compressed & uncompressed)\n");
  strcat(libInfo.engineAbilities,
         "[14] Exportable 2D File Formats : TGA(24-Bit, uncompressed)\n");
  strcat(libInfo.engineAbilities,
         "[15] UI implementation including fonts\n");
  strcat(libInfo.engineAbilities,
         "[16] Win32 native application framework");
  strcat(libInfo.engineAbilities,
         "\n");
  strcat(libInfo.engineAbilities,
         "\n");
  strcat(libInfo.engineAbilities,
         "For further informations see https://code.google.com/p/lighthouse3d/\n");

  if (fileHandler.loadImageToPicture(_lighthouse3DLogo_, &logo) == true)
	  libSetup.showLogo = 1;
  else
	  libSetup.showLogo = 0;

  libSetup.colDetect = _noCollisionDetection_;

#ifdef __MULTI_CORE_SUPPORT
  for (i=0; i<__CPU_CORES_SUPPORTED; i++) {
    screen2DCPU[i] = new C2DScreen;
    zBufferCPU[i] = new CZBuffer;
	fogSynthesisCPU[i] = new CFogSynthesis;
  }
#endif
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::~C3DLibrary( ... )
///////////////////////////////////////////////////////////////////////////////
C3DLibrary::~C3DLibrary()
{
  release();
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::release( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::release()
{
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupUseInternalScreen( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupUseInternalScreen()
{
  libSetup.usedScreen = _useInternal2DScreen_;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupColDetection( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupColDetection(int ColDetection)
{
  libSetup.colDetect = ColDetection;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupBFCulling( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupBFCulling(int bfCulling)
{
  libSetup.bfCulling = bfCulling;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupTexFilter( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupFSAAFilter(int filter)
{
  libSetup.fsAA = filter;
}
///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupTexFilter( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupTexFilter(int filter)
{
  libSetup.texFilter = filter;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupLighting( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupLighting(int lighting)
{
  libSetup.texLighting = lighting;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupRenderer( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupRenderer(int renderer)
{
  libSetup.texRenderer = renderer;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupShader( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupShader(int shader)
{
  libSetup.texShader = shader;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::FogSynthesis( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupFogSynthesis(int fog)
{
  libSetup.fogSynthesis = fog;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setupIncShader( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setupIncShader()
{

  switch (libSetup.texShader)
  {
     case _noTexShader_:
     {
        setupShader(_lambertTexShader_);
        break;
     }
     case _lambertTexShader_:
     {
        setupShader(_gouraudTexShader_);
        break;
     }
     case _gouraudTexShader_:
     {
        setupShader(_phongTexShader_);
        break;
     }
     case _phongTexShader_:
     {
        setupShader(_noTexShader_);
        break;
     }
  }

}

void C3DLibrary::setupLogo(int logo)
{
	libSetup.showLogo = logo;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setPositionMap( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setPositionMap(int mesh, T3DPosition posMap)
{
  if (&m_meshStorage == NULL)
      return;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return;

  if (posMap.xr < 0)
	  posMap.xr = 360 + posMap.xr;
  if (posMap.yr < 0)
	  posMap.yr = 360 + posMap.yr;
  if (posMap.zr < 0)
	  posMap.zr = 360 + posMap.zr;

  m_meshStorage.getMesh(mesh)->get3DPosition()->rot_changed = false;
  if (posMap.xr != m_meshStorage.getMesh(mesh)->get3DPosition()->xr)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->rot_changed = true;
  if (posMap.yr != m_meshStorage.getMesh(mesh)->get3DPosition()->yr)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->rot_changed = true;
  if (posMap.zr != m_meshStorage.getMesh(mesh)->get3DPosition()->zr)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->rot_changed = true;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xr = posMap.xr;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yr = posMap.yr;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zr = posMap.zr;

  m_meshStorage.getMesh(mesh)->get3DPosition()->skal_changed = false;
  if (posMap.xs != m_meshStorage.getMesh(mesh)->get3DPosition()->xs)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->skal_changed = true;
  if (posMap.ys != m_meshStorage.getMesh(mesh)->get3DPosition()->ys)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->skal_changed = true;
  if (posMap.zs != m_meshStorage.getMesh(mesh)->get3DPosition()->zs)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->skal_changed = true;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xs = posMap.xs;
  m_meshStorage.getMesh(mesh)->get3DPosition()->ys = posMap.ys;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zs = posMap.zs;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xs_rel *= posMap.xs;
  m_meshStorage.getMesh(mesh)->get3DPosition()->ys_rel *= posMap.ys;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zs_rel *= posMap.zs;

  m_meshStorage.getMesh(mesh)->get3DPosition()->trans_changed = false;
  if (posMap.xt != m_meshStorage.getMesh(mesh)->get3DPosition()->xt)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->trans_changed = true;
  if (posMap.yt != m_meshStorage.getMesh(mesh)->get3DPosition()->yt)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->trans_changed = true;
  if (posMap.zt != m_meshStorage.getMesh(mesh)->get3DPosition()->zt)
	  m_meshStorage.getMesh(mesh)->get3DPosition()->trans_changed = true;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xt = posMap.xt;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yt = posMap.yt;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zt = posMap.zt;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xt_rel += posMap.xt;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yt_rel += posMap.yt;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zt_rel += posMap.zt;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::getPositionMap( ... )
///////////////////////////////////////////////////////////////////////////////
T3DPosition C3DLibrary::getPositionMap(int mesh)
{
  T3DPosition p;

  p.xr = m_meshStorage.getMesh(mesh)->get3DPosition()->xr;
  p.yr = m_meshStorage.getMesh(mesh)->get3DPosition()->yr;
  p.zr = m_meshStorage.getMesh(mesh)->get3DPosition()->zr;

  p.xs = m_meshStorage.getMesh(mesh)->get3DPosition()->xs;
  p.ys = m_meshStorage.getMesh(mesh)->get3DPosition()->ys;
  p.zs = m_meshStorage.getMesh(mesh)->get3DPosition()->zs;

  p.xs_rel = m_meshStorage.getMesh(mesh)->get3DPosition()->xs_rel;
  p.ys_rel = m_meshStorage.getMesh(mesh)->get3DPosition()->ys_rel;
  p.zs_rel = m_meshStorage.getMesh(mesh)->get3DPosition()->zs_rel;

  p.xt = m_meshStorage.getMesh(mesh)->get3DPosition()->xt;
  p.yt = m_meshStorage.getMesh(mesh)->get3DPosition()->yt;
  p.zt = m_meshStorage.getMesh(mesh)->get3DPosition()->zt;

  p.xt_rel = m_meshStorage.getMesh(mesh)->get3DPosition()->xt_rel;
  p.yt_rel = m_meshStorage.getMesh(mesh)->get3DPosition()->yt_rel;
  p.zt_rel = m_meshStorage.getMesh(mesh)->get3DPosition()->zt_rel;

  return p;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::clearPositionMap( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::clearPositionMap(int mesh)
{
  if (&m_meshStorage == NULL)
      return;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xr = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yr = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zr = 0;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xs = 1;
  m_meshStorage.getMesh(mesh)->get3DPosition()->ys = 1;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zs = 1;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xt = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yt = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zt = 0;

  m_meshStorage.getMesh(mesh)->get3DPosition()->rot_changed = true;
  m_meshStorage.getMesh(mesh)->get3DPosition()->skal_changed = true;
  m_meshStorage.getMesh(mesh)->get3DPosition()->trans_changed = true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::initPositionMap( ... )
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::initPositionMap(int mesh)
{
  if (&m_meshStorage == NULL)
      return;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xr = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yr = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zr = 0;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xs = 1;
  m_meshStorage.getMesh(mesh)->get3DPosition()->ys = 1;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zs = 1;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xs_rel = 1;
  m_meshStorage.getMesh(mesh)->get3DPosition()->ys_rel = 1;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zs_rel = 1;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xt = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yt = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zt = 0;

  m_meshStorage.getMesh(mesh)->get3DPosition()->xt_rel = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->yt_rel = 0;
  m_meshStorage.getMesh(mesh)->get3DPosition()->zt_rel = 0;

  m_meshStorage.getMesh(mesh)->get3DPosition()->rot_changed = true;
  m_meshStorage.getMesh(mesh)->get3DPosition()->skal_changed = true;
  m_meshStorage.getMesh(mesh)->get3DPosition()->trans_changed = true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::addNewMesh( ... )
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::addNewMesh()
{
  if (&m_meshStorage == NULL)
      return false;

  if(!m_meshStorage.addNewMesh())
  {
      return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::removeMesh( ... )
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::removeMesh(int mesh)
{
  if (&m_meshStorage == NULL)
      return false;

  if(!m_meshStorage.removeMesh(mesh))
  {
      return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::addNewVertex( ... )
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::addNewVertex(int mesh, TMeshVertex vert)
{
  if (&m_meshStorage == NULL)
      return false;

  if(!m_meshStorage.getMesh(mesh))
  {
      return false;
  }

  m_meshStorage.getMesh(mesh)->addVert(vert);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::addNewTriangle( ... )
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::addNewTriangle(int mesh, TMeshTriangle face)
{
  if (&m_meshStorage == NULL)
      return false;

  if(!m_meshStorage.getMesh(mesh))
  {
      return false;
  }

  m_meshStorage.getMesh(mesh)->addFace(face);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::getMesh( ... )
///////////////////////////////////////////////////////////////////////////////
CMeshData* C3DLibrary::getMesh(int mesh)
{
  if (&m_meshStorage == NULL)
      return NULL;

  return m_meshStorage.getMesh(mesh);
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::getMeshCount( ... )
///////////////////////////////////////////////////////////////////////////////
int C3DLibrary::getMeshCount()
{
  return m_meshStorage.getMeshCount();
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::getTexCount( ... )
///////////////////////////////////////////////////////////////////////////////
int C3DLibrary::getTexCount()
{
  return m_meshStorage.getTexCount();
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::getVertex( ... )
///////////////////////////////////////////////////////////////////////////////
TMeshVertex* C3DLibrary::getVertex(int mesh, int vert)
{
  if (&m_meshStorage == NULL)
      return NULL;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return NULL;

  return m_meshStorage.getMesh(mesh)->getVert(vert);
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::getTriangle( ... )
///////////////////////////////////////////////////////////////////////////////
TMeshTriangle* C3DLibrary::getTriangle(int mesh, int face)
{
  if (&m_meshStorage == NULL)
      return NULL;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return NULL;

  return m_meshStorage.getMesh(mesh)->getFace(face);
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setChangedVertex( ... )
///////////////////////////////////////////////////////////////////////////////
// set manipulated data
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::setChangedVertex(int mesh, int vert, TMeshVertex vertex)
{
  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh)->getVert(vert) == NULL)
      return false;

  m_meshStorage.getMesh(mesh)->getVert(vert)->xt = vertex.xt;
  m_meshStorage.getMesh(mesh)->getVert(vert)->yt = vertex.yt;
  m_meshStorage.getMesh(mesh)->getVert(vert)->zt = vertex.zt;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setScreenVertex( ... )
///////////////////////////////////////////////////////////////////////////////
// set screen data
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::setScreenVertex(int mesh, int vert, TMeshVertex vertex)
{
  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh)->getVert(vert) == NULL)
      return false;

  m_meshStorage.getMesh(mesh)->getVert(vert)->sx = vertex.sx;
  m_meshStorage.getMesh(mesh)->getVert(vert)->sy = vertex.sy;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setVertex( ... )
///////////////////////////////////////////////////////////////////////////////
// set original data
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::setVertex(int mesh, int vert, TMeshVertex vertex)
{
  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh)->getVert(vert) == NULL)
      return false;

  m_meshStorage.getMesh(mesh)->getVert(vert)->x = vertex.x;
  m_meshStorage.getMesh(mesh)->getVert(vert)->y = vertex.y;
  m_meshStorage.getMesh(mesh)->getVert(vert)->z = vertex.z;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::rotateOriginalData( ... )
///////////////////////////////////////////////////////////////////////////////
// rotates one mesh around 0 and stores it in the original data
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::rotateOriginalData(int mesh, int rx, int ry, int rz)
{
  int i;
  TMeshVertex *v;
  
  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  if (rx < 0)
      rx = 360 + rx;
  if (rx > 359)
      rx = rx - 360;
  if (ry < 0)
      ry = 360 + ry;
  if (ry > 359)
      ry = ry - 360;
  if (rz < 0)
      rz = 360 + rz;
  if (rz > 359)
      rz = rz - 360;

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
    v = m_meshStorage.getMesh(mesh)->getVert(i);

    v->xt = v->x;
    v->yt = v->y;
    v->zt = v->z;
    
    if ( mathe3D.rotation(v, rx, ry, rz) == false )
        return false;
        
    v->x = v->xt;
    v->y = v->yt;
    v->z = v->zt;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::projection( ... )
///////////////////////////////////////////////////////////////////////////////
// projects one mesh
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::projection(int mesh)
{
  int i;
  TMeshVertex *v;

  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
    v = m_meshStorage.getMesh(mesh)->getVert(i);

    if ( mathe3D.projectionCamera(v, (float) opticalCorrectness, screenCenterX, screenCenterY) == false )
        return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::rotation( ... )
///////////////////////////////////////////////////////////////////////////////
// rotates one mesh around 0
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::rotation(int mesh)
{
  int i;
  TMeshVertex *v;
  CMeshData *pmesh;
  
  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);

  if (pmesh == NULL)
	  return false;

  for (i = 0; i < pmesh->getVertCount(); i++)
  {
	v = pmesh->getVert(i);

	if ( mathe3D.rotation(v,
						  pmesh->get3DPosition()->xr,
						  pmesh->get3DPosition()->yr,
						  pmesh->get3DPosition()->zr
                          ) == false )
        return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::rotationAroundP( ... )
///////////////////////////////////////////////////////////////////////////////
// rotates one mesh around P
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::rotationAroundP(int mesh, TMeshVertex p, int rx, int ry, int rz)
{
  int i;
  TMeshVertex *v;
  
  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  if (rx < 0)
      rx = 360 + rx;
  if (rx > 359)
      rx = rx - 360;
  if (ry < 0)
      ry = 360 + ry;
  if (ry > 359)
      ry = ry - 360;
  if (rz < 0)
      rz = 360 + rz;
  if (rz > 359)
      rz = rz - 360;

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
    v = m_meshStorage.getMesh(mesh)->getVert(i);

    if ( mathe3D.translation(v, p.x, p.y, p.z
                             ) == false )
        return false;

    if ( mathe3D.rotation(v, rx, ry, rz
                          ) == false )
        return false;

    if ( mathe3D.translation(v, -p.x, -p.y, -p.z
                             ) == false )
		return false;

  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::rotationVAroundP( ... )
///////////////////////////////////////////////////////////////////////////////
// rotates one vertex v around p
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::rotationVAroundP(TMeshVertex *v, TMeshVertex p, int rx, int ry, int rz)
{
  if (rx < 0)
      rx = 360 + rx;
  if (rx > 359)
      rx = rx - 360;
  if (ry < 0)
      ry = 360 + ry;
  if (ry > 359)
      ry = ry - 360;
  if (rz < 0)
      rz = 360 + rz;
  if (rz > 359)
      rz = rz - 360;

  if ( mathe3D.translation(v, p.x, p.y, p.z) == false )
      return false;

  if ( mathe3D.rotation(v, rx, ry, rz) == false )
      return false;

  if ( mathe3D.translation(v, -p.x, -p.y, -p.z) == false )
	  return false;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::skalation( ... )
///////////////////////////////////////////////////////////////////////////////
// skales one mesh
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::skalation(int mesh)
{
  int i;
  TMeshVertex *v;
  CMeshData *pmesh;

  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);
  if (pmesh == NULL)
	  return false;

  for (i = 0; i < pmesh->getVertCount(); i++)
  {
	v = pmesh->getVert(i);

    if ( mathe3D.skalation(v,
						   pmesh->get3DPosition()->xs,
						   pmesh->get3DPosition()->ys,
						   pmesh->get3DPosition()->zs
						   ) == false )
		return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::translation( ... )
///////////////////////////////////////////////////////////////////////////////
// translates one mesh
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::translation(int mesh)
{
  int i;
  TMeshVertex *v;
  CMeshData *pmesh;

  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);
  if (pmesh == NULL)
	  return false;

  for (i = 0; i < pmesh->getVertCount(); i++)
  {
	v = pmesh->getVert(i);

	if ( mathe3D.translation(v,
							 pmesh->get3DPosition()->xt,
							 pmesh->get3DPosition()->yt,
							 pmesh->get3DPosition()->zt
							 ) == false )
		return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::translateToOrigin( ... )
///////////////////////////////////////////////////////////////////////////////
// translates one mesh back to origin
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::translateToOrigin(int mesh)
{
  int i;
  TMeshVertex *v;

  if (&m_meshStorage == NULL)
	  return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
	  return false;

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
	v = m_meshStorage.getMesh(mesh)->getVert(i);

	if ( mathe3D.translation(v,
							 -m_meshStorage.getMesh(mesh)->get3DPosition()->xt_rel,
							 -m_meshStorage.getMesh(mesh)->get3DPosition()->yt_rel,
							 -m_meshStorage.getMesh(mesh)->get3DPosition()->zt_rel
							 ) == false )
		return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::translateToPosition( ... )
///////////////////////////////////////////////////////////////////////////////
// translates one mesh back to its real position (counter function to
// translateToOrigin)
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::translateToPosition(int mesh)
{
  int i;
  TMeshVertex *v;

  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
	  return false;

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
	v = m_meshStorage.getMesh(mesh)->getVert(i);

	if ( mathe3D.translation(v,
							 m_meshStorage.getMesh(mesh)->get3DPosition()->xt_rel,
							 m_meshStorage.getMesh(mesh)->get3DPosition()->yt_rel,
							 m_meshStorage.getMesh(mesh)->get3DPosition()->zt_rel
							 ) == false )
		return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::translation( ... )
///////////////////////////////////////////////////////////////////////////////
// sklation, rotation, translation for one mesh at once
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::complgeometricops(int mesh)
{
  int i;
  TMeshVertex *v;
  CMeshData *pmesh;

  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);
  if (pmesh == NULL)
	  return false;

  for (i = 0; i < pmesh->getVertCount(); i++)
  {
	v = pmesh->getVert(i);

	if ( mathe3D.translation(v,
							 -pmesh->get3DPosition()->xt_rel,
							 -pmesh->get3DPosition()->yt_rel,
							 -pmesh->get3DPosition()->zt_rel
							 ) == false )
		return false;

	if (pmesh->get3DPosition()->skal_changed)
		if ( mathe3D.skalation(v,
								 pmesh->get3DPosition()->xs,
								 pmesh->get3DPosition()->ys,
								 pmesh->get3DPosition()->zs
								 ) == false )
			return false;

	if (pmesh->get3DPosition()->rot_changed)
		if ( mathe3D.rotation(v,
								 pmesh->get3DPosition()->xr,
								 pmesh->get3DPosition()->yr,
								 pmesh->get3DPosition()->zr
								 ) == false )
			return false;

	if ( mathe3D.translation(v,
							 pmesh->get3DPosition()->xt_rel,
							 pmesh->get3DPosition()->yt_rel,
							 pmesh->get3DPosition()->zt_rel
							 ) == false )
		return false;

	if (pmesh->get3DPosition()->trans_changed)
		if ( mathe3D.translation(v,
								 pmesh->get3DPosition()->xt,
								 pmesh->get3DPosition()->yt,
								 pmesh->get3DPosition()->zt
								 ) == false )
			return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::transformCamera( ... )
///////////////////////////////////////////////////////////////////////////////
// transforms a mesh relative to the Camera
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::transformCamera(int mesh)
{
  int i;
  int collisionDetected;

  TCamera *vp;
  TMeshVertex p;
  TMeshVertex *v;
  TVector3D vec;
  TMeshVertex cameraVector;
  TVector3D distanceVector;
  float distance, sqr_max_dist;


  if (&m_meshStorage == NULL)
	  return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
	  return false;

  vp = cameraHandler.getCamera(cameraHandler.getActiveCamera());

  // prepare the camera (you are sitting in the origin of the camera space)
  cameraVector.xt = vp->camOriginX;
  cameraVector.yt = vp->camOriginY;
  cameraVector.zt = vp->camOriginZ;

  collisionDetected = 0;
  sqr_max_dist = cameraHandler.getColDistance(-1) * cameraHandler.getColDistance(-1);

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
	v = m_meshStorage.getMesh(mesh)->getVert(i);

	if ( mathe3D.translationCamera(v, vp->trX, vp->trY, vp->trZ) == false )
		return false;
  }

  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
	v = m_meshStorage.getMesh(mesh)->getVert(i);

	vec.d[0] = v->xtc;
	vec.d[1] = v->ytc;
	vec.d[2] = v->ztc;

	cameraHandler.transformVector(&vec, -1);

	v->xtc = vec.d[0];
	v->ytc = vec.d[1];
	v->ztc = vec.d[2];

	if ( libSetup.colDetect ) {

		distanceVector.d[0] = -cameraVector.xt - v->xtc;
		distanceVector.d[1] = -cameraVector.yt - v->ytc;
		distanceVector.d[2] = -cameraVector.zt - v->ztc;

		// Using the dotProduct means saving the slow sqrt
		distance = mathe3D.dotProduct(distanceVector, distanceVector);

		if ( distance < sqr_max_dist )
			  collisionDetected = 1;
	}
  }

  if (collisionDetected == 1) {

	  cameraHandler.undoLastMove(-1);

	  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
	  {
		v = m_meshStorage.getMesh(mesh)->getVert(i);

		if ( mathe3D.translationCamera(v, vp->trX, vp->trY, vp->trZ) == false )
			return false;
	  }

	  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
	  {
		v = m_meshStorage.getMesh(mesh)->getVert(i);

		vec.d[0] = v->xtc;
		vec.d[1] = v->ytc;
		vec.d[2] = v->ztc;

		cameraHandler.transformVector(&vec, -1);

		v->xtc = vec.d[0];
		v->ytc = vec.d[1];
		v->ztc = vec.d[2];

	 }
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::transformNormals( ... )
///////////////////////////////////////////////////////////////////////////////
// transforms the vertex and plane normals relativ to the Camera
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::transformNormals(int mesh)
{
  int i;

  TMeshVertex v, *vt;
  TMeshTriangle *f;
  TVector3D vec;
  CMeshData *pmesh;

  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);

  if ( pmesh == NULL)
	  return false;

  if ( ( pmesh->get3DPosition()->xr != 0 ) ||
	   ( pmesh->get3DPosition()->yr != 0 ) ||
	   ( pmesh->get3DPosition()->zr != 0 ) )
  {
	  for (i = 0; i < pmesh->getFaceCount(); i++)
	  {
		   f = pmesh->getFace(i);

		   v.xt = f->nvt[0];
		   v.yt = f->nvt[1];
		   v.zt = f->nvt[2];

		   if ( mathe3D.rotation(&v,
							 pmesh->get3DPosition()->xr,
							 pmesh->get3DPosition()->yr,
							 pmesh->get3DPosition()->zr
							 ) == false )
				return false;

		   f->nvt[0] = v.xt;
		   f->nvt[1] = v.yt;
		   f->nvt[2] = v.zt;
	   }
  }

  for (i = 0; i < pmesh->getFaceCount(); i++)
  {
	   f = pmesh->getFace(i);

	   vec.d[0] = f->nvt[0];
	   vec.d[1] = f->nvt[1];
	   vec.d[2] = f->nvt[2];

	   cameraHandler.transformVector(&vec, -1);

	   f->nvtc[0] = vec.d[0];
	   f->nvtc[1] = vec.d[1];
	   f->nvtc[2] = vec.d[2];
  }

  if ( ( pmesh->get3DPosition()->xr != 0 ) ||
	   ( pmesh->get3DPosition()->yr != 0 ) ||
	   ( pmesh->get3DPosition()->zr != 0 ) )
  {
	   for (i = 0; i < pmesh->getVertCount(); i++)
	   {
			vt = pmesh->getVert(i);

			v.xt = vt->vnxt;
			v.yt = vt->vnyt;
			v.zt = vt->vnzt;

			if ( mathe3D.rotation(&v,
									pmesh->get3DPosition()->xr,
									 pmesh->get3DPosition()->yr,
									 pmesh->get3DPosition()->zr
									 ) == false )
						   	 return false;

			vt->vnxt = v.xt;
			vt->vnyt = v.yt;
			vt->vnzt = v.zt;
	   }
  }

  for (i = 0; i < pmesh->getVertCount(); i++)
  {
	   vt = pmesh->getVert(i);

	   vec.d[0] = vt->vnxt;
	   vec.d[1] = vt->vnyt;
	   vec.d[2] = vt->vnzt;

	   cameraHandler.transformVector(&vec, -1);

	   vt->vnxtc = vec.d[0];
	   vt->vnytc = vec.d[1];
	   vt->vnztc = vec.d[2];
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::backFaceCulling( ... )
///////////////////////////////////////////////////////////////////////////////
// Removes the Face not seen from the active Camera
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::backFaceCulling(int mesh)
{
  float skpView;
  int i;

  TMeshTriangle *f;
  TMeshVertex cameraVector;
  TMeshVertex ViewRay, v;
  TVector3D ViewVector;
  CMeshData *pmesh;
  TMeshVertex *pvertex0, *pvertex1, *pvertex2;
  TCamera *vp;

  float invthree = mathe3D.fastinv((float)3);

  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);

  if ( pmesh == NULL)
	  return false;

  if ( ( libSetup.bfCulling ==  _noBackFaceCulling_) ||
	   ( libSetup.texRenderer != _texRenderer_ ) )
  {
	  for (i = 0; i < pmesh->getFaceCount(); i++)
	  {
		   f = pmesh->getFace(i);

		   f->bfCulled = false;
	  }

	  return true;
  }

  vp = cameraHandler.getCamera(cameraHandler.getActiveCamera());

  // prepare the camera
  cameraVector.xt = vp->camOriginX;
  cameraVector.yt = vp->camOriginY;
  cameraVector.zt = vp->camOriginZ;

  for (i = 0; i < pmesh->getFaceCount(); i++)
  {
	   f = pmesh->getFace(i);
	   if (!f->zClipped) {

		   pvertex0 = pmesh->getVert(f->v[0]);
		   pvertex1 = pmesh->getVert(f->v[1]);
		   pvertex2 = pmesh->getVert(f->v[2]);

		   ViewRay.x =  -cameraVector.xt - (((pvertex0->xtc)+
											 (pvertex1->xtc)+
											 (pvertex2->xtc)) * invthree);
		   ViewRay.y =  -cameraVector.yt - (((pvertex0->ytc)+
											 (pvertex1->ytc)+
											 (pvertex2->ytc)) * invthree);
		   ViewRay.z =  -cameraVector.zt - (((pvertex0->ztc)+
											 (pvertex1->ztc)+
											 (pvertex2->ztc)) * invthree);

		  ViewVector = mathe3D.normalizeVector(mathe3D.doV3D(ViewRay.x,ViewRay.y,ViewRay.z));
		  skpView = mathe3D.dotProduct(mathe3D.doV3D(ViewVector.d[0],ViewVector.d[1],ViewVector.d[2]),
									   mathe3D.doV3D(f->nvtc[0], f->nvtc[1], f->nvtc[2]));
		  // Back faces won't be paint!
		  if ( skpView < 0)
			f->bfCulled = true;
		  else
			f->bfCulled = false;
	   }
	   else
			f->bfCulled = true;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::zClipping( ... )
///////////////////////////////////////////////////////////////////////////////
// removes the faces which are too near or too far from the active Camera
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::zClipping(int mesh)
{
  int i;

  TCamera *vp;
  TMeshTriangle *f;
  CMeshData *pmesh;
  TMeshVertex *pvertex0, *pvertex1, *pvertex2;

  if (&m_meshStorage == NULL)
	  return false;

  pmesh = m_meshStorage.getMesh(mesh);

  if ( pmesh == NULL)
	  return false;

  vp = cameraHandler.getCamera(cameraHandler.getActiveCamera());

  for (i = 0; i < pmesh->getFaceCount(); i++)
  {
	   f = pmesh->getFace(i);

	   pvertex0 = pmesh->getVert(f->v[0]);
	   pvertex1 = pmesh->getVert(f->v[1]);
	   pvertex2 = pmesh->getVert(f->v[2]);

	   f->zClipped = false;
	   if ( (pvertex0->ztc < vp->nearClipping ) ||
			(pvertex1->ztc < vp->nearClipping ) ||
			(pvertex2->ztc < vp->nearClipping ) )
			 f->zClipped = true;
	   if ( (pvertex0->ztc > vp->farClipping ) ||
			(pvertex1->ztc > vp->farClipping ) ||
			(pvertex2->ztc > vp->farClipping ) )
			 f->zClipped = true;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::geometricPreparation( ... )
///////////////////////////////////////////////////////////////////////////////
// prepares the camera transformed values of the vertexes (xt,yt,zt)
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::geometricPreparation(int mesh)
{
  int i;

  TMeshVertex *v;

  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  // Reset the transformed values to the original values
  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
       v = m_meshStorage.getMesh(mesh)->getVert(i);

       v->xt = v->x;
	   v->yt = v->y;
	   v->zt = v->z;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::resetMesh( ... )
///////////////////////////////////////////////////////////////////////////////
// resets the transformed mesh to its original data
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::resetMesh(int mesh)
{
	return geometricPreparation(mesh);
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::transformLights( ... )
///////////////////////////////////////////////////////////////////////////////
// Transforms dotLights relativ to the Camera vp
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::transformLights()
{
  int i;

  TCamera *vp;
  TMeshVertex v;
  TLightSource l;
  TVector3D vec;

  vp = cameraHandler.getCamera(cameraHandler.getActiveCamera());

  for (i = 0; i < lightingHandler.getDotLightCount(); i++ )
  {
       v.xt = lightingHandler.getDotLight(i)->x;
       v.yt = lightingHandler.getDotLight(i)->y;
       v.zt = lightingHandler.getDotLight(i)->z;

       if ( mathe3D.translation(&v, vp->trX, vp->trY, vp->trZ) == false )
           return false;

       vec.d[0] = v.xt;
	   vec.d[1] = v.yt;
       vec.d[2] = v.zt;
    
	   cameraHandler.transformVector(&vec, -1);

       v.xt = vec.d[0];
	   v.yt = vec.d[1];
	   v.zt = vec.d[2];

       l.xt = v.xt;
	   l.yt = v.yt;
	   l.zt = v.zt;

	   lightingHandler.setTransformedDotLight(i, l);
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::calcPlaneNormals( ... )
///////////////////////////////////////////////////////////////////////////////
// Calculates the plane normals for one mesh
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::calcPlaneNormals(int mesh)
{
  float x1, y1, z1;
  float x2, y2, z2;
  float x3, y3, z3;
  int i;

  TMeshTriangle *f;
  TVector3D pNormal;

  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
	  return false;

  // Calculate the plane normals
  for (i = 0; i < m_meshStorage.getMesh(mesh)->getFaceCount(); i++)
  {
       f = m_meshStorage.getMesh(mesh)->getFace(i);

	   if (f->v[0] > m_meshStorage.getMesh(mesh)->getVertCount() )
		   return false;
	   if (f->v[1] > m_meshStorage.getMesh(mesh)->getVertCount() )
		   return false;
	   if (f->v[2] > m_meshStorage.getMesh(mesh)->getVertCount() )
		   return false;

	   x1 = m_meshStorage.getMesh(mesh)->getVert(f->v[0])->xt;
       y1 = m_meshStorage.getMesh(mesh)->getVert(f->v[0])->yt;
       z1 = m_meshStorage.getMesh(mesh)->getVert(f->v[0])->zt;
       x2 = m_meshStorage.getMesh(mesh)->getVert(f->v[1])->xt;
	   y2 = m_meshStorage.getMesh(mesh)->getVert(f->v[1])->yt;
	   z2 = m_meshStorage.getMesh(mesh)->getVert(f->v[1])->zt;
       x3 = m_meshStorage.getMesh(mesh)->getVert(f->v[2])->xt;
	   y3 = m_meshStorage.getMesh(mesh)->getVert(f->v[2])->yt;
       z3 = m_meshStorage.getMesh(mesh)->getVert(f->v[2])->zt;

	   pNormal = mathe3D.crossProduct(mathe3D.doV3D(x2-x1,y2-y1,z2-z1),mathe3D.doV3D(x3-x1,y3-y1,z3-z1));
	   pNormal = mathe3D.normalizeVector(pNormal);

       f->nv[0] = pNormal.d[0];
       f->nv[1] = pNormal.d[1];
       f->nv[2] = pNormal.d[2];

	   // We need those as we are working with them from version 0.5.1.1 on
	   f->nvt[0] = pNormal.d[0];
       f->nvt[1] = pNormal.d[1];
	   f->nvt[2] = pNormal.d[2];
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::calcVertexNormals( ... )
///////////////////////////////////////////////////////////////////////////////
// Calculates the vertex normals for one mesh
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::calcVertexNormals(int mesh)
{
  float n, nvx, nvy, nvz;
  int i,j;

  TMeshVertex *v;
  TMeshTriangle *f;

  if (&m_meshStorage == NULL)
      return false;

  if (m_meshStorage.getMesh(mesh) == NULL)
      return false;

  // Calculate the vertex normals
  for (i = 0; i < m_meshStorage.getMesh(mesh)->getVertCount(); i++)
  {
 	   v = m_meshStorage.getMesh(mesh)->getVert(i);

       n = 0;
	   nvx = 0;
       nvy = 0;
       nvz = 0;

	   for (j = 0; j < m_meshStorage.getMesh(mesh)->getFaceCount(); j++)
       {
			f = m_meshStorage.getMesh(mesh)->getFace(j);

		   if (f->v[0] > m_meshStorage.getMesh(mesh)->getVertCount() )
			   return false;
		   if (f->v[1] > m_meshStorage.getMesh(mesh)->getVertCount() )
			   return false;
		   if (f->v[2] > m_meshStorage.getMesh(mesh)->getVertCount() )
		       return false;

            if ( f->v[0] == i ) 
            {
                n = n + 1;
                nvx = nvx + f->nv[0];
				nvy = nvy + f->nv[1];
                nvz = nvz + f->nv[2];
            }
            if ( f->v[1] == i ) 
			{
                n = n + 1;
                nvx = nvx + f->nv[0];
				nvy = nvy + f->nv[1];
                nvz = nvz + f->nv[2];
            }
			if ( f->v[2] == i )
            {
				n = n + 1;
                nvx = nvx + f->nv[0];
                nvy = nvy + f->nv[1];
				nvz = nvz + f->nv[2];
            }
	   }

	   if ( n != 0 ) {
		   v->vnx = nvx / n;
           v->vny = nvy / n;
           v->vnz = nvz / n;           

		   // We need those as we are working with them from version 0.5.1.1 on
		   v->vnxt = nvx / n;
           v->vnyt = nvy / n;
		   v->vnzt = nvz / n;
       }
	   else
       {
           v->vnx = 0;
           v->vny = 0;
		   v->vnz = 0;

		   // We need those as we are working with them from version 0.5.1.1 on
		   v->vnxt = 0;
		   v->vnyt = 0;
           v->vnzt = 0;
       }
  }
  
  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::renderPixelCPU( ... )
///////////////////////////////////////////////////////////////////////////////
// renders a pixel per cpu
///////////////////////////////////////////////////////////////////////////////
#ifdef __MULTI_CORE_SUPPORT
bool C3DLibrary::renderPixelCPU(unsigned char cpu, int x, int y, unsigned int color)
{
  renderedPixelsCount++;
  screen2DCPU[cpu]->getScreenHandle()->d[y*screenSizeX+x] = color;

  return true;
}
#endif

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::renderPixel( ... )
///////////////////////////////////////////////////////////////////////////////
// renders a pixel
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::renderPixel(int x, int y, unsigned int color)
{
  renderedPixelsCount++;
  screen2D.getScreenHandle()->d[y*screenSizeX+x] = color;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::renderLine( ... )
///////////////////////////////////////////////////////////////////////////////
// renders a line
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::renderLine(int x1, int y1, int x2, int y2, unsigned int color)
{
  if ( (y1 < 0) || (y1>=screenSizeY) )
	  return true;
  if ( (x1 < 0) || (x1>=screenSizeX) )
	  return true;

  if ( (y2 < 0) || (y2>=screenSizeY) )
	  return true;
  if ( (x2 < 0) || (x2>=screenSizeX) )
	  return true;

  screen2D.drawLine(x1,y1,x2,y2,color);

  return true;
}

bool C3DLibrary::renderBox(int x1, int y1, int x2, int y2, int y3,unsigned int color1, unsigned int color2, unsigned int color3)
{
  screen2D.drawBox(x1,y1,x2,y2,y3,color1,color2,color3);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::renderHLine( ... )
///////////////////////////////////////////////////////////////////////////////
// renders a horizontal line
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::renderHLine(unsigned char cpu, TLineData *LT)
{
  float l, linv;
  float x3D1, y3D1, z3D1;
  float x3D2, y3D2, z3D2;
  float dx3D, dy3D, dz3D;
  float uT3D1, vT3D1, zT3D1;
  float uT3D2, vT3D2, zT3D2;
  float duT3D, dvT3D, dzT3D;
  float dvTex;

  float uTL, vTL, uTR, vTR, zTL, zTR;
  float uTT, vTT, uTB, vTB, zTT, zTB;
  float uTC, vTC, zTC;

  float intLambert;
  float intLambertPhLR, intLambertPhLG, intLambertPhLB;
  float Lint, Rint, dInt;
  float LintR, RintR, dIntR;
  float LintG, RintG, dIntG;
  float LintB, RintB, dIntB;
  float LnvX, RnvX, dNvX;
  float LnvY, RnvY, dNvY;
  float LnvZ, RnvZ, dNvZ;

  int x1,x2,x,y,i;
  int uTexture, vTexture;
  int uLTexture, vLTexture;
  int uRTexture, vRTexture;
  int uBTexture, vBTexture;
  int uTTexture, vTTexture;
  int uCTexture, vCTexture;

  float intPhong, skpPhong;
  float intPhongPhLR, intPhongPhLG, intPhongPhLB, skpPhongPhL;
  float vLength1, vLength2, dvLength;

  unsigned int color;
  unsigned int colorLeft, colorRight, colorTop, colorBottom, colorCenter;
  unsigned int colorRed, colorGreen, colorBlue;

  TRGBColorTriple colorBuffer;

  unsigned int tmpIntConv;
  float convf;

  CFilter f;
  TBilinearMap dtMap, liDefMap, biDefMap;
  // TCamera *vp;
  TLightSource *s;
  TMeshVertex cameraVector;
  TMeshTexture *pTex;
  TMeshVertex LightRay;
  TMeshVertex ViewRay;
  TRGBColorTriple rgbColorLeft, rgbColorRight, rgbColorTop, rgbColorBottom, rgbColorCenter;
  TVector3D LightVector;
  TVector3D NormalVector;
  TVector3D RVector;
  TVector3D ViewVector;

  f.liFDefMapOne(&liDefMap);
  f.blFDefMapOne(&biDefMap);

  renderedHLinesCount++;

  // Where is really left and right...?
  if (LT->Lx < LT->Rx)
  {
	  x1 = LT->Lx;
	  x2 = LT->Rx;
	  y = LT->y;
	  pTex = LT->tex;
	  dvTex = LT->dvTex;
	  intLambert = LT->intLambert;
      intLambertPhLR = LT->intLambertPhLR;
      intLambertPhLG = LT->intLambertPhLG;
	  intLambertPhLB = LT->intLambertPhLB;
	  Lint = LT->Lint;
	  Rint = LT->Rint;
	  LintR = LT->LintR;
	  LintG = LT->LintG;
	  LintB = LT->LintB;
      RintR = LT->RintR;
      RintG = LT->RintG;
	  RintB = LT->RintB;
	  LnvX = LT->LnvX;
	  LnvY = LT->LnvY;
      LnvZ = LT->LnvZ;
	  RnvX = LT->RnvX;
	  RnvY = LT->RnvY;
	  RnvZ = LT->RnvZ;
	  x3D1 = LT->Lx3D;
      y3D1 = LT->Ly3D;
	  z3D1 = LT->Lz3D;
	  x3D2 = LT->Rx3D;
	  y3D2 = LT->Ry3D;
	  z3D2 = LT->Rz3D;
	  uT3D1 = LT->LuT3D;
	  vT3D1 = LT->LvT3D;
      zT3D1 = LT->LzT3D;
      uT3D2 = LT->RuT3D;
      vT3D2 = LT->RvT3D;
	  zT3D2 = LT->RzT3D;
  }
  else
  {
	  x2 = LT->Lx;
      x1 = LT->Rx;
	  y = LT->y;
	  pTex = LT->tex;
      dvTex = LT->dvTex;
	  intLambert = LT->intLambert;
	  intLambertPhLR = LT->intLambertPhLR;
	  intLambertPhLG = LT->intLambertPhLG;
	  intLambertPhLB = LT->intLambertPhLB;
	  Rint = LT->Lint;
	  Lint = LT->Rint;
      RintR = LT->LintR;
	  RintG = LT->LintG;
      RintB = LT->LintB;
	  LintR = LT->RintR;
	  LintG = LT->RintG;
	  LintB = LT->RintB;
	  RnvX = LT->LnvX;
      RnvY = LT->LnvY;
	  RnvZ = LT->LnvZ;
      LnvX = LT->RnvX;
	  LnvY = LT->RnvY;
	  LnvZ = LT->RnvZ;
      x3D2 = LT->Lx3D;
	  y3D2 = LT->Ly3D;
	  z3D2 = LT->Lz3D;
	  x3D1 = LT->Rx3D;
	  y3D1 = LT->Ry3D;
	  z3D1 = LT->Rz3D;
	  uT3D2 = LT->LuT3D;
	  vT3D2 = LT->LvT3D;
	  zT3D2 = LT->LzT3D;
	  uT3D1 = LT->RuT3D;
	  vT3D1 = LT->RvT3D;
	  zT3D1 = LT->RzT3D;
  }

  if ( (y<0) || (y>=screenSizeY) ) {
	  return true;
  }

  if ( (uT3D1 < 0) || (vT3D1 < 0) || (uT3D2 < 0) || (vT3D2 < 0) ) {
	  return true;
  }

  l = (float)(x2-x1);
  if (l <= 0) {
	  return true;
  }

  if ( pTex == NULL ) {
	  return true;
  }

  s = lightingHandler.getAmbientLight();

  if (libSetup.fogSynthesis != _noFogSynthesis_ )
  {
     ViewRay.x =  -cameraVector.xt - x3D1;
	 ViewRay.y =  -cameraVector.yt - y3D1;
	 ViewRay.z =  -cameraVector.zt - z3D1;

	 vLength1 = mathe3D.dotProduct(mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z),
                                   mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z));

	 ViewRay.x =  -cameraVector.xt - x3D2;
	 ViewRay.y =  -cameraVector.yt - y3D2;
	 ViewRay.z =  -cameraVector.zt - z3D2;

	 vLength2 = mathe3D.dotProduct(mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z),
								   mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z));

	 // dvLength = (vLength2 - vLength1) / l;
  }
  else
  {
	 vLength1 = 0;
	 vLength2 = 0;
	 // dvLength = 1;
  }

  linv = 1/l;

  dx3D = (x3D2 - x3D1) * linv;
  dy3D = (y3D2 - y3D1) * linv;
  dz3D = (z3D2 - z3D1) * linv;

  duT3D = (uT3D2 - uT3D1) * linv;
  dvT3D = (vT3D2 - vT3D1) * linv;
  dzT3D = (zT3D2 - zT3D1) * linv;

  dInt = (Rint - Lint) * linv;
  dIntR = (RintR - LintR) * linv;
  dIntG = (RintG - LintG) * linv;
  dIntB = (RintB - LintB) * linv;

  if (libSetup.texShader == _phongTexShader_)
  {
	dNvX = (RnvX - LnvX) * linv;
	dNvY = (RnvY - LnvY) * linv;
	dNvZ = (RnvZ - LnvZ) * linv;
  }

  dvLength = (vLength2 - vLength1) * linv;

  if ( x2 > screenSizeX )
	x2 = screenSizeX;

  for (x = x1; x <= x2; x++)
  {

	   // Not required: Has been checked before
	   // if ( z3D1 < vp->nearClipping )
	   //	   return true;
	   if ( x >= 0 )
	   {
#ifdef __MULTI_CORE_SUPPORT
//		   if ( z3D1 < zBufferCPU[cpu]->getZ(x, y) )
		   if ( z3D1 < zBufferCPU[cpu]->getBufferHandle()->d[y*screenSizeX+x])
#else
//		   if ( z3D1 < zBuffer.getZ(x, y) )
		   if ( z3D1 < zBuffer.getBufferHandle()->d[y*screenSizeX+x])
#endif
		   {
#ifdef __MULTI_CORE_SUPPORT
//			   zBufferCPU[cpu]->setZ(x, y, z3D1);
			   zBufferCPU[cpu]->getBufferHandle()->d[y*screenSizeX+x] = z3D1;
#else
//			   zBuffer.setZ(x, y, z3D1);
			   zBuffer.getBufferHandle()->d[y*screenSizeX+x] = z3D1;
#endif

		       if ( zT3D1 != 0 )
			   {

				   if (libSetup.texFilter == _noTexFilter_)
				   {
					   // Replaced by the routine below for speed increasement
					   // uTexture = ((int)(( uT3D1 / zT3D1 ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   // vTexture = ((int)(( vT3D1 / zT3D1 ) * (float)pTex->m_height)) % (int)pTex->m_height;

					   float zT3D1inv = (1 / zT3D1);

					   convf = (( uT3D1 * zT3D1inv ) * (float)pTex->m_width);
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp tmpIntConv
					   };
#else
					   tmpIntConv = (unsigned int) convf;
#endif
					   uTexture = tmpIntConv % pTex->m_width;

					   convf = (( vT3D1 * zT3D1inv ) * (float)pTex->m_height);
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp tmpIntConv
					   };
#else
					   tmpIntConv = (unsigned int) convf;
#endif
					   vTexture = tmpIntConv % pTex->m_height;

#ifdef __MULTI_CORE_SUPPORT
					   colorBuffer = screen2DCPU[cpu]->getRGBColor(pTex->m_image[vTexture*pTex->m_width+uTexture]);
#else
					   colorBuffer = screen2D.getRGBColor(pTex->m_image[vTexture*pTex->m_width+uTexture]);
#endif

					   colorRed = colorBuffer.r;
					   colorGreen = colorBuffer.g;
					   colorBlue = colorBuffer.b;
				   }

				   if (libSetup.texFilter == _linearTexFilter_)
				   {

					   // Linear texture filtering
					   uTC = uT3D1;
					   vTC = vT3D1;
					   zTC = zT3D1;
					   uTL = uT3D1-duT3D;
					   vTL = vT3D1;
					   zTL = zT3D1-dzT3D;
					   uTR = uT3D1+duT3D;
					   vTR = vT3D1;
					   zTR = zT3D1+dzT3D;
					   if (uTL < 0) uTL = 0;
					   if (uTR > 1) uTL = 1;
					   if (vTR < 0) vTR = 0;
					   if (vTR > 1) vTR = 1;
					   if (zTL < 0) zTL = 0.01;
					   if (zTL > 1) zTL = 1;
					   if (zTR < 0) zTR = 0.01;
					   if (zTR > 1) zTR = 1;

					   // Position of texture left, right and center
					   uLTexture = ((int)(( uTL / zTL ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   vLTexture = ((int)(( vTL / zTL ) * (float)pTex->m_height)) % (int)pTex->m_height;
					   uRTexture = ((int)(( uTR / zTR ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   vRTexture = ((int)(( vTR / zTR ) * (float)pTex->m_height)) % (int)pTex->m_height;
					   uCTexture = ((int)(( uTC / zTC ) * (float)pTex->m_width)) % (int)pTex->m_width;
                       vCTexture = ((int)(( vTC / zTC ) * (float)pTex->m_height)) % (int)pTex->m_height;

                       colorLeft = (unsigned int) pTex->m_image[vLTexture*pTex->m_width+uLTexture];
					   colorRight = (unsigned int) pTex->m_image[vRTexture*pTex->m_width+uRTexture];
					   colorCenter = (unsigned int) pTex->m_image[vCTexture*pTex->m_width+uCTexture];

					   // decode colors to rgbcolor triple
#ifdef __MULTI_CORE_SUPPORT
					   rgbColorLeft = screen2DCPU[cpu]->getRGBColor(colorLeft);
					   rgbColorRight = screen2DCPU[cpu]->getRGBColor(colorRight);
                       rgbColorCenter = screen2DCPU[cpu]->getRGBColor(colorCenter);
#else
					   rgbColorLeft = screen2D.getRGBColor(colorLeft);
					   rgbColorRight = screen2D.getRGBColor(colorRight);
                       rgbColorCenter = screen2D.getRGBColor(colorCenter);
#endif

					   // filter rgb colors separated!
					   dtMap.d[1][0] = 0;
					   dtMap.d[1][2] = 0;

					   dtMap.d[0][1] = (float) rgbColorLeft.r;
                       dtMap.d[1][1] = (float) rgbColorCenter.r;
					   dtMap.d[2][1] = (float) rgbColorRight.r;
					   colorRed = (unsigned int) f.bilinearFilter(liDefMap, dtMap);

					   dtMap.d[0][1] = (float) rgbColorLeft.g;
					   dtMap.d[1][1] = (float) rgbColorCenter.g;
					   dtMap.d[2][1] = (float) rgbColorRight.g;
					   colorGreen = (unsigned int) f.bilinearFilter(liDefMap, dtMap);

                       dtMap.d[0][1] = (float) rgbColorLeft.b;
					   dtMap.d[1][1] = (float) rgbColorCenter.b;
                       dtMap.d[2][1] = (float) rgbColorRight.b;
					   colorBlue = (unsigned int) f.bilinearFilter(liDefMap, dtMap);

				   }

				   if (libSetup.texFilter == _bilinearTexFilter_)
				   {

					   // Bilinear texture filtering
					   uTC = uT3D1;
					   vTC = vT3D1;
					   zTC = zT3D1;
					   uTL = uT3D1-duT3D;
					   vTL = vT3D1;
                       zTL = zT3D1-dzT3D;
					   uTR = uT3D1+duT3D;
					   vTR = vT3D1;
                       zTR = zT3D1+dzT3D;
					   uTT = uT3D1;
					   vTT = vT3D1-dvTex;
					   zTT = zT3D1;
                       uTB = uT3D1;
                       vTB = vT3D1+dvTex;
                       zTB = zT3D1;
					   if (uTL < 0) uTL = 0;
                       if (uTR > 1) uTR = 1;
					   if (vTR < 0) vTR = 0;
                       if (vTR > 1) vTR = 1;
					   if (zTL < 0) zTL = 0.01;
					   if (zTR > 1) zTR = 1;
					   if (vTT < 0) vTT = 0;
					   if (vTT > 1) vTT = 1;
                       if (vTB < 0) vTB = 0;
					   if (vTB > 1) vTB = 1;

					   // Position of texture left, right, top, bottom and center
					   uLTexture = ((int)(( uTL / zTL ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   vLTexture = ((int)(( vTL / zTL ) * (float)pTex->m_height)) % (int)pTex->m_height;
					   uRTexture = ((int)(( uTR / zTR ) * (float)pTex->m_width)) % (int)pTex->m_width;
                       vRTexture = ((int)(( vTR / zTR ) * (float)pTex->m_height)) % (int)pTex->m_height;
					   uBTexture = ((int)(( uTB / zTB ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   vBTexture = ((int)(( vTB / zTB ) * (float)pTex->m_height)) % (int)pTex->m_height;
                       uTTexture = ((int)(( uTT / zTT ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   vTTexture = ((int)(( vTT / zTT ) * (float)pTex->m_height)) % (int)pTex->m_height;
					   uCTexture = ((int)(( uTC / zTC ) * (float)pTex->m_width)) % (int)pTex->m_width;
					   vCTexture = ((int)(( vTC / zTC ) * (float)pTex->m_height)) % (int)pTex->m_height;

                       colorLeft = (unsigned int) pTex->m_image[vLTexture*pTex->m_width+uLTexture];
                       colorRight = (unsigned int) pTex->m_image[vRTexture*pTex->m_width+uRTexture];
					   colorTop = (unsigned int) pTex->m_image[vTTexture*pTex->m_width+uTTexture];
                       colorBottom = (unsigned int) pTex->m_image[vBTexture*pTex->m_width+uBTexture];
					   colorCenter = (unsigned int) pTex->m_image[vCTexture*pTex->m_width+uCTexture];

#ifdef __MULTI_CORE_SUPPORT
					   // decode colors to rgbcolor triple
					   rgbColorLeft = screen2DCPU[cpu]->getRGBColor(colorLeft);
					   rgbColorRight = screen2DCPU[cpu]->getRGBColor(colorRight);
					   rgbColorTop = screen2DCPU[cpu]->getRGBColor(colorTop);
                       rgbColorBottom = screen2DCPU[cpu]->getRGBColor(colorBottom);
					   rgbColorCenter = screen2DCPU[cpu]->getRGBColor(colorCenter);
#else
					   // decode colors to rgbcolor triple
					   rgbColorLeft = screen2D.getRGBColor(colorLeft);
					   rgbColorRight = screen2D.getRGBColor(colorRight);
					   rgbColorTop = screen2D.getRGBColor(colorTop);
                       rgbColorBottom = screen2D.getRGBColor(colorBottom);
					   rgbColorCenter = screen2D.getRGBColor(colorCenter);
#endif
					   // filter rgb colors separated!

					   dtMap.d[0][1] = (float) rgbColorLeft.r;
					   dtMap.d[2][1] = (float) rgbColorRight.r;
                       dtMap.d[1][1] = (float) rgbColorCenter.r;
					   dtMap.d[1][0] = (float) rgbColorTop.r;
					   dtMap.d[1][2] = (float) rgbColorBottom.r;
                       colorRed = (unsigned int) f.bilinearFilter(biDefMap, dtMap);

					   dtMap.d[0][1] = (float) rgbColorLeft.g;
					   dtMap.d[2][1] = (float) rgbColorRight.g;
					   dtMap.d[1][1] = (float) rgbColorCenter.g;
                       dtMap.d[1][0] = (float) rgbColorTop.g;
					   dtMap.d[1][2] = (float) rgbColorBottom.g;
					   colorGreen = (unsigned int) f.bilinearFilter(biDefMap, dtMap);

					   dtMap.d[0][1] = (float) rgbColorLeft.b;
					   dtMap.d[2][1] = (float) rgbColorRight.b;
					   dtMap.d[1][1] = (float) rgbColorCenter.b;
					   dtMap.d[1][0] = (float) rgbColorTop.b;
					   dtMap.d[1][2] = (float) rgbColorBottom.b;
					   colorBlue = (unsigned int) f.bilinearFilter(biDefMap, dtMap);

				   }

				   if (libSetup.texShader == _noTexShader_)
				   {
					   color = colorRed + 256*colorGreen + 65536*colorBlue;
				   }

				   if (libSetup.texShader == _lambertTexShader_)
				   {
						convf = ( (colorRed * intLambert) + intLambertPhLR );
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp colorRed
					   };
#else
					   colorRed = (unsigned int) convf;
#endif
					   // colorRed = (unsigned int) ( (colorRed * intLambert) + intLambertPhLR );
					   if ( colorRed > 255 )
							colorRed = 255;

						convf = ( (colorGreen * intLambert) + intLambertPhLG );
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp colorGreen
					   };
#else
					   colorGreen = (unsigned int) convf;
#endif
					   // colorGreen = (unsigned int) ( (colorGreen * intLambert) + intLambertPhLG );
					   if ( colorGreen > 255 )
							colorGreen = 255;

						convf = ( (colorBlue * intLambert) + intLambertPhLB );
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp colorBlue
					   };
#else
					   colorBlue = (unsigned int) convf;
#endif
					   colorBlue = (unsigned int) ( (colorBlue * intLambert) + intLambertPhLB );

					   // Not required here
					   // ViewRay.x =  -cameraVector.xt - x3D1;
					   // ViewRay.y =  -cameraVector.yt - y3D1;
					   // ViewRay.z =  -cameraVector.zt - z3D1;

#ifdef __MULTI_CORE_SUPPORT                                     
					   if (libSetup.fogSynthesis == _linearFogSynthesis_)
						   fogSynthesisCPU[cpu]->linearFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);

					   if (libSetup.fogSynthesis == _exponentialFogSynthesis_)
                           fogSynthesisCPU[cpu]->exponentialFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);					   
#else
					   if (libSetup.fogSynthesis == _linearFogSynthesis_)
						   fogSynthesis.linearFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);

					   if (libSetup.fogSynthesis == _exponentialFogSynthesis_)
						   fogSynthesis.exponentialFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);
#endif

					   color = (colorRed + 256*colorGreen + 65536*colorBlue);
				   }

				   if (libSetup.texShader == _gouraudTexShader_)
				   {
						convf = ( (colorRed * Lint) + LintR );
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp colorRed
					   };
#else
					   colorRed = (unsigned int) convf;
#endif
					   // colorRed = (unsigned int) ( (colorRed * Lint) + LintR );
					   if ( colorRed > 255 )
							colorRed = 255;

						convf = ( (colorGreen * Lint) + LintR );
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp colorGreen
					   };
#else
					   colorGreen = (unsigned int) convf;
#endif
					   // colorGreen = (unsigned int) ( (colorGreen * Lint) + LintG );
					   if ( colorGreen > 255 )
							colorGreen = 255;

						convf = ( (colorBlue * Lint) + LintR );
#ifdef __I386_ASM_OPTIMIZATION
					   // Fast conversion float to int
					   __asm
					   {
							fld convf
							fistp colorBlue
					   };
#else
					   colorBlue = (unsigned int) convf;
#endif
					   // colorBlue = (unsigned int) ( (colorBlue * Lint) + LintB );
					   if ( colorBlue > 255 )
							colorBlue = 255;

					   // Not required here
					   // ViewRay.x =  -cameraVector.xt - x3D1;
					   // ViewRay.y =  -cameraVector.yt - y3D1;
					   // ViewRay.z =  -cameraVector.zt - z3D1;

#ifdef __MULTI_CORE_SUPPORT                                     
					   if (libSetup.fogSynthesis == _linearFogSynthesis_)
						   fogSynthesisCPU[cpu]->linearFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);

					   if (libSetup.fogSynthesis == _exponentialFogSynthesis_)
                           fogSynthesisCPU[cpu]->exponentialFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);					   
#else
					   if (libSetup.fogSynthesis == _linearFogSynthesis_)
						   fogSynthesis.linearFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);

					   if (libSetup.fogSynthesis == _exponentialFogSynthesis_)
						   fogSynthesis.exponentialFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);
#endif
					   color = (colorRed + 256*colorGreen + 65536*colorBlue);
				   }

				   if (libSetup.texShader == _phongTexShader_)
				   {

					   intPhong = s->ia;

					   intPhongPhLR = 0;
					   intPhongPhLG = 0;
					   intPhongPhLB = 0;

					   for (i=0; i<lightingHandler.getDotLightCount(); i++)
					   {
							LightRay.x = lightingHandler.getDotLight(i)->xt - x3D1;
							LightRay.y = lightingHandler.getDotLight(i)->yt - y3D1;
							LightRay.z = lightingHandler.getDotLight(i)->zt - z3D1;

							LightVector = mathe3D.normalizeVector(
                                          mathe3D.doV3D(LightRay.x,LightRay.y,LightRay.z));
							skpPhong = mathe3D.dotProduct(mathe3D.doV3D(LightVector.d[0],LightVector.d[1],LightVector.d[2]),
															 mathe3D.doV3D(LnvX, LnvY, LnvZ));
                                         
							if ( skpPhong < 0 ) skpPhong *= 0;

							intPhong = intPhong + lightingHandler.getDotLight(i)->id*skpPhong;

							ViewRay.x =  -cameraVector.xt - x3D1;
							ViewRay.y =  -cameraVector.yt - y3D1;
							ViewRay.z =  -cameraVector.zt - z3D1;

							if ( ( libSetup.texLighting == _phongLighting_ ) && ( skpPhong != 0 ) ) {

								ViewVector = mathe3D.normalizeVector(mathe3D.doV3D(ViewRay.x,ViewRay.y,ViewRay.z));

								NormalVector.d[0] = LnvX;
								NormalVector.d[1] = LnvY;
								NormalVector.d[2] = LnvZ;

								RVector = mathe3D.normalizeVector(
										  mathe3D.subVectors(
										  mathe3D.multSkVector(2*skpPhong,NormalVector),
										  LightVector));

								skpPhongPhL = mathe3D.dotProduct(mathe3D.doV3D(ViewVector.d[0],ViewVector.d[1],ViewVector.d[2]),
													 mathe3D.doV3D(RVector.d[0], RVector.d[1], RVector.d[2]));
                                                 
                                if ( skpPhongPhL < 0 ) skpPhongPhL *= 0;

								intPhongPhLR = intPhongPhLR +
                                   lightingHandler.getDotLight(i)->r *
								   lightingHandler.getDotLight(i)->is *
								   mathe3D.powerof(skpPhongPhL, lightingHandler.getSpecularPower());
                                intPhongPhLG = intPhongPhLG + 
								   lightingHandler.getDotLight(i)->g *
								   lightingHandler.getDotLight(i)->is *
                                   mathe3D.powerof(skpPhongPhL, lightingHandler.getSpecularPower());
								intPhongPhLB = intPhongPhLB +
								   lightingHandler.getDotLight(i)->b *
                                   lightingHandler.getDotLight(i)->is *
								   mathe3D.powerof(skpPhongPhL, lightingHandler.getSpecularPower());
                            }

                       }
      
                       if (intPhongPhLR > 255) intPhongPhLR = 255;
					   if (intPhongPhLG > 255) intPhongPhLG = 255;
					   if (intPhongPhLB > 255) intPhongPhLB = 255;

					   if (intPhong > 1)
						   intPhong = 1;

                       colorRed = (unsigned int) ( (colorRed * intPhong) + intPhongPhLR );
					   if ( colorRed > 255 )
							colorRed = 255;
                       colorGreen = (unsigned int) ( (colorGreen * intPhong) + intPhongPhLG );
					   if ( colorGreen > 255 )
                            colorGreen = 255;
                       colorBlue = (unsigned int) ( (colorBlue * intPhong) + intPhongPhLB );
					   if ( colorBlue > 255 )
							colorBlue = 255;

#ifdef __MULTI_CORE_SUPPORT                                     
					   if (libSetup.fogSynthesis == _linearFogSynthesis_)
						   fogSynthesisCPU[cpu]->linearFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);

					   if (libSetup.fogSynthesis == _exponentialFogSynthesis_)
                           fogSynthesisCPU[cpu]->exponentialFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);					   
#else
					   if (libSetup.fogSynthesis == _linearFogSynthesis_)
						   fogSynthesis.linearFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);

					   if (libSetup.fogSynthesis == _exponentialFogSynthesis_)
                           fogSynthesis.exponentialFogSynthesis(vLength1, colorRed, colorGreen, colorBlue);
#endif

					   color = (colorRed + 256*colorGreen + 65536*colorBlue);
                   }
#ifdef __MULTI_CORE_SUPPORT                                     
				   renderPixelCPU(cpu, x, y, color);
#else
				   renderPixel(x, y, color);
#endif
			   }
		   }  
	   }

	   x3D1 += dx3D;
	   y3D1 += dy3D;
	   z3D1 += dz3D;

	   uT3D1 += duT3D;
	   vT3D1 += dvT3D;
	   zT3D1 += dzT3D;

	   Lint += dInt;

	   if (libSetup.fogSynthesis != _noFogSynthesis_ )
		   vLength1 += dvLength;

	   if (libSetup.texShader == _gouraudTexShader_)
	   {
		   LintR += dIntR;
		   LintG += dIntG;
		   LintB += dIntB;
	   }

	   if (libSetup.texShader == _phongTexShader_)
	   {
		   LnvX += dNvX;
		   LnvY += dNvY;
		   LnvZ += dNvZ;
	   }
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::renderTriangle( ... )
///////////////////////////////////////////////////////////////////////////////
// renders a triangle of a mesh
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::renderTriangle(unsigned char cpu, int face)
{
  // Numbers of the triangles unsorted
  int p1,p2,p3;
  int Lx, Rx, i;
  
  float dsxP1P2, dsyP1P2;
  float dsxP1P3, dsyP1P3;
  float dsxP2P3, dsyP2P3;
  float dxP1P2, dyP1P2, dzP1P2;
  float dxP1P3, dyP1P3, dzP1P3;
  float dxP2P3, dyP2P3, dzP2P3;
  float xP1P2, yP1P2, zP1P2;
  float xP1P3, yP1P3, zP1P3;
  float xP2P3, yP2P3, zP2P3;
  float uTexP1, vTexP1, zTexP1;
  float uTexP2, vTexP2, zTexP2;
  float uTexP3, vTexP3, zTexP3;
  float uTP1P2, vTP1P2, zTP1P2;
  float uTP1P3, vTP1P3, zTP1P3;
  float uTP2P3, vTP2P3, zTP2P3;
  float duTP1P2, dvTP1P2, dzTP1P2;
  float duTP1P3, dvTP1P3, dzTP1P3;
  float duTP2P3, dvTP2P3, dzTP2P3;
  float nzTP1, nzTP2, nzTP3;
  float dvTex;

  float skpLambert;
  float skpLambertPhL;
  float intLambert;
  float intLambertPhLR, intLambertPhLG, intLambertPhLB;
  float skpGouraudV1, skpGouraudV2, skpGouraudV3;
  float skpGouraudV1PhL, skpGouraudV2PhL, skpGouraudV3PhL;
  float intGouraudV1, intGouraudV2, intGouraudV3;
  float intGouraudV1PhLR, intGouraudV1PhLG, intGouraudV1PhLB;
  float intGouraudV2PhLR, intGouraudV2PhLG, intGouraudV2PhLB;
  float intGouraudV3PhLR, intGouraudV3PhLG, intGouraudV3PhLB;
  float intP1P2, intP1P3, intP2P3;
  float dintP1P2, dintP1P3, dintP2P3;
  float intP1P2PhLR, intP1P2PhLG, intP1P2PhLB;
  float dintP1P2PhLR, dintP1P2PhLG, dintP1P2PhLB;
  float intP1P3PhLR, intP1P3PhLG, intP1P3PhLB;
  float dintP1P3PhLR, dintP1P3PhLG, dintP1P3PhLB;
  float intP2P3PhLR, intP2P3PhLG, intP2P3PhLB;
  float dintP2P3PhLR, dintP2P3PhLG, dintP2P3PhLB;
  float nv1XPhongS, nv1YPhongS, nv1ZPhongS;
  float nv2XPhongS, nv2YPhongS, nv2ZPhongS;
  float nv3XPhongS, nv3YPhongS, nv3ZPhongS;
  float nvXP1P2PhS, nvYP1P2PhS, nvZP1P2PhS;
  float nvXP1P3PhS, nvYP1P3PhS, nvZP1P3PhS;
  float nvXP2P3PhS, nvYP2P3PhS, nvZP2P3PhS;
  float dnvXP1P2PhS, dnvYP1P2PhS, dnvZP1P2PhS;
  float dnvXP1P3PhS, dnvYP1P3PhS, dnvZP1P3PhS;
  float dnvXP2P3PhS, dnvYP2P3PhS, dnvZP2P3PhS;

  float dsyP1P2inv, dsyP1P3inv, dsyP2P3inv;

  CMeshData *md;
  TCamera *vp;
  TLightSource *s;
  TMeshTriangle *pt;
  TMeshTexture *pTex;
  TMeshVertex cameraVector;
  TMeshVertex *p1s, *p2s, *p3s;
  TMeshVertex LightRay;
  TMeshVertex ViewRay;
  TVector3D LightVector;
  TVector3D NormalVector;
  TVector3D RVector;
  TVector3D ViewVector;

  TLineData LT;

  if (&m_meshStorage == NULL)
	  return false;

  if (m_meshStorage.getMesh(zOrderTrList[face].mesh) == NULL)
	  return false;

  md = m_meshStorage.getMesh(zOrderTrList[face].mesh);

  vp = cameraHandler.getCamera(cameraHandler.getActiveCamera());

  // prepare the camera 
  cameraVector.xt = vp->camOriginX;
  cameraVector.yt = vp->camOriginY;
  cameraVector.zt = vp->camOriginZ;

  pt = zOrderTrList[face].t;
  pTex = m_meshStorage.getTex(pt->texture);

  s = lightingHandler.getAmbientLight();

  // Get the texture area
  p1 = pt->v[0];
  md->getVert(p1)->u = pt->ut[0];
  md->getVert(p1)->v = pt->vt[0];

  p2 = pt->v[1];
  md->getVert(p2)->u = pt->ut[1];
  md->getVert(p2)->v = pt->vt[1];

  p3 = pt->v[2];
  md->getVert(p3)->u = pt->ut[2];
  md->getVert(p3)->v = pt->vt[2];

  // Is this triangle completely below the already painted screen?
  // Bug: Due to some unknown this routine removes faces needed to be rendered, too.
  /*
  if ( (md->getVert(p1)->ztc-_deltaZ_ > zBuffer.getZ(md->getVert(p1)->sx,md->getVert(p1)->sy)) &&
	   (md->getVert(p2)->ztc-_deltaZ_ > zBuffer.getZ(md->getVert(p2)->sx,md->getVert(p2)->sy)) &&
	   (md->getVert(p3)->ztc-_deltaZ_ > zBuffer.getZ(md->getVert(p3)->sx,md->getVert(p3)->sy)) )
	  return true;
  */

  // Is this triangle completely below outside the painted screen?
  if ( (md->getVert(p1)->sx < 0) &&
	   (md->getVert(p2)->sx < 0) &&
	   (md->getVert(p3)->sx < 0) )
	  return true;
  if ( (md->getVert(p1)->sx >= screenSizeX) &&
	   (md->getVert(p2)->sx >= screenSizeX) &&
	   (md->getVert(p3)->sx >= screenSizeX) )
	  return true;
  if ( (md->getVert(p1)->sy < 0) &&
	   (md->getVert(p2)->sy < 0) &&
	   (md->getVert(p3)->sy < 0) )
	  return true;
  if ( (md->getVert(p1)->sy >= screenSizeY) &&
	   (md->getVert(p2)->sy >= screenSizeY) &&
	   (md->getVert(p3)->sy >= screenSizeY) )
	  return true;

  // Sort the vertexes of the triangle
  p1s = MinPoint3(md->getVert(p1),md->getVert(p2),md->getVert(p3));
  p2s = MidPoint3(md->getVert(p2),md->getVert(p3),md->getVert(p1));
  p3s = MaxPoint3(md->getVert(p3),md->getVert(p1),md->getVert(p2));
  if(md->getVert(p2)->sy < md->getVert(p1)->sy)
  {
	p1s = MinPoint3(md->getVert(p2),md->getVert(p1),md->getVert(p3));
	p2s = MidPoint3(md->getVert(p1),md->getVert(p3),md->getVert(p2));
  }

  // Clearing the variables

  xP1P2 = 1;
  yP1P2 = 1;
  zP1P2 = 1;
  // xP1P3 = 1;
  // yP1P3 = 1;
  // zP1P3 = 1;
  xP2P3 = 1;
  yP2P3 = 1;
  zP2P3 = 1;
  dxP1P2 = 0;
  dyP1P2 = 0;
  dzP1P2 = 0;
  dxP1P3 = 0;
  dyP1P3 = 0;
  dzP1P3 = 0;
  dxP2P3 = 0;
  dyP2P3 = 0;
  dzP2P3 = 0;

  uTP1P2 = 1;
  vTP1P2 = 1;
  zTP1P2 = 1;
  // uTP1P3 = 1;
  // vTP1P3 = 1;
  // zTP1P3 = 1;
  uTP2P3 = 1;
  vTP2P3 = 1;
  zTP2P3 = 1;
  duTP1P2 = 0;
  dvTP1P2 = 0;
  dzTP1P2 = 0;
  duTP1P3 = 0;
  dvTP1P3 = 0;
  dzTP1P3 = 0;
  duTP2P3 = 0;
  dvTP2P3 = 0;
  dzTP2P3 = 0;

  intP1P2 = 1;
  intP1P3 = 1;
  intP2P3 = 1;
  dintP1P2 = 0;
  dintP1P3 = 0;
  dintP2P3 = 0;

  intP1P2PhLR = 1;
  intP1P2PhLG = 1;
  intP1P2PhLB = 1;
  intP1P3PhLR = 1;
  intP1P3PhLG = 1;
  intP1P3PhLB = 1;
  intP2P3PhLR = 1;
  intP2P3PhLG = 1;
  intP2P3PhLB = 1;
  dintP1P2PhLR = 0;
  dintP1P2PhLG = 0;
  dintP1P2PhLB = 0;
  dintP1P3PhLR = 0;
  dintP1P3PhLG = 0;
  dintP1P3PhLB = 0;
  dintP2P3PhLR = 0;
  dintP2P3PhLG = 0;
  dintP2P3PhLB = 0;

  nvXP1P2PhS = 1;
  nvYP1P2PhS = 1;
  nvZP1P2PhS = 1;
  nvXP1P3PhS = 1;
  nvYP1P3PhS = 1;
  nvZP2P3PhS = 1;
  nvXP2P3PhS = 1;
  nvYP2P3PhS = 1;
  nvZP1P3PhS = 1;
  dnvXP1P2PhS = 0;
  dnvYP1P2PhS = 0;
  dnvZP1P2PhS = 0;
  dnvXP1P3PhS = 0;
  dnvYP1P3PhS = 0;
  dnvZP1P3PhS = 0;
  dnvXP2P3PhS = 0;
  dnvYP2P3PhS = 0;
  dnvZP2P3PhS = 0;

  // Intializing finished

  dsxP1P2 = (float) (p2s->sx - p1s->sx);
  dsyP1P2 = (float) (p2s->sy - p1s->sy);
  dsxP1P3 = (float) (p3s->sx - p1s->sx);
  dsyP1P3 = (float) (p3s->sy - p1s->sy);
  dsxP2P3 = (float) (p3s->sx - p2s->sx);
  dsyP2P3 = (float) (p3s->sy - p2s->sy);

  if ( dsyP1P3 <= 0 )
      return true;

  // x/y/z values in the start vertexes
  xP1P2 = p1s->xtc;
  yP1P2 = p1s->ytc;
  zP1P2 = p1s->ztc;
  xP1P3 = p1s->xtc;
  yP1P3 = p1s->ytc;
  zP1P3 = p1s->ztc;
  xP2P3 = p2s->xtc;
  yP2P3 = p2s->ytc;
  zP2P3 = p2s->ztc;

  // x/y/z incs
  dxP1P3 = (p3s->xtc - p1s->xtc) / dsyP1P3;
  dyP1P3 = (p3s->ytc - p1s->ytc) / dsyP1P3;
  dzP1P3 = (p3s->ztc - p1s->ztc) / dsyP1P3;

  // textures
  nzTP1 = p1s->ztc;
  if ( nzTP1 == 0 )
      nzTP1 = 1;
  zTexP1 = (float) 1.0 / nzTP1;
  uTexP1 = p1s->u * zTexP1;
  vTexP1 = p1s->v * zTexP1;

  nzTP2 = p2s->ztc;
  if ( nzTP2 == 0 )
	  nzTP2 = 1;
  zTexP2 = (float) 1.0 / nzTP2;
  uTexP2 = p2s->u * zTexP2;
  vTexP2 = p2s->v * zTexP2;

  nzTP3 = p3s->ztc;
  if ( nzTP3 == 0 )
      nzTP3 = 1;
  zTexP3 = (float) 1.0 / nzTP3;
  uTexP3 = p3s->u * zTexP3;
  vTexP3 = p3s->v * zTexP3;

  // u/v start value for ray p1p2, p1p3 && p2p3
  uTP1P2 = uTexP1;
  vTP1P2 = vTexP1;
  zTP1P2 = zTexP1;
  uTP1P3 = uTexP1;
  vTP1P3 = vTexP1;
  zTP1P3 = zTexP1;
  uTP2P3 = uTexP2;
  vTP2P3 = vTexP2;
  zTP2P3 = zTexP2;

  duTP1P3 = (uTexP3 - uTexP1) / dsyP1P3;
  dvTP1P3 = (vTexP3 - vTexP1) / dsyP1P3;
  dzTP1P3 = (zTexP3 - zTexP1) / dsyP1P3;

  // preparation for LAMBERT shading (LAMBERT shading and PHONG lighting)
  if (libSetup.texShader == _lambertTexShader_)
  {

	  intLambert = s->ia;

	  intLambertPhLR = 0;
	  intLambertPhLG = 0;
      intLambertPhLB = 0;

	  for (i=0; i<lightingHandler.getDotLightCount(); i++)
	  {
		   LightRay.x = lightingHandler.getDotLight(i)->xt - (((p1s->xtc)+
						  (p2s->xtc)+
						  (p3s->xtc)) / 3);
           LightRay.y = lightingHandler.getDotLight(i)->yt - (((p1s->ytc)+
                          (p2s->ytc)+
                          (p3s->ytc)) / 3);
           LightRay.z = lightingHandler.getDotLight(i)->zt - (((p1s->ztc)+
						  (p2s->ztc)+
                          (p3s->ztc)) / 3);

		   LightVector = mathe3D.normalizeVector(mathe3D.doV3D(LightRay.x,LightRay.y,LightRay.z));
           skpLambert = mathe3D.dotProduct(mathe3D.doV3D(LightVector.d[0],LightVector.d[1],LightVector.d[2]),
										   mathe3D.doV3D(pt->nvtc[0], pt->nvtc[1], pt->nvtc[2]));
                                         
		   if ( skpLambert < 0 ) skpLambert *= 0;

		   intLambert = intLambert + lightingHandler.getDotLight(i)->id*skpLambert;

           ViewRay.x =  -cameraVector.xt - (((p1s->xtc)+(p2s->xtc)+(p3s->xtc)) / 3);
		   ViewRay.y =  -cameraVector.yt - (((p1s->ytc)+(p2s->ytc)+(p3s->ytc)) / 3);
           ViewRay.z =  -cameraVector.zt - (((p1s->ztc)+(p2s->ztc)+(p3s->ztc)) / 3);

		   if ( ( libSetup.texLighting == _phongLighting_ ) && ( skpLambert != 0 ) ) {
		   
			   ViewVector = mathe3D.normalizeVector(mathe3D.doV3D(ViewRay.x,ViewRay.y,ViewRay.z));

			   NormalVector.d[0] = pt->nvtc[0];
			   NormalVector.d[1] = pt->nvtc[1];
               NormalVector.d[2] = pt->nvtc[2];

               RVector = mathe3D.normalizeVector(
						 mathe3D.subVectors(
                         mathe3D.multSkVector(2*skpLambert,NormalVector),
						 LightVector));

			   skpLambertPhL = mathe3D.dotProduct(mathe3D.doV3D(ViewVector.d[0],ViewVector.d[1],ViewVector.d[2]),
                                                  mathe3D.doV3D(RVector.d[0], RVector.d[1], RVector.d[2]));

               if ( skpLambertPhL < 0 ) skpLambertPhL *= 0;

			   intLambertPhLR = intLambertPhLR +
								lightingHandler.getDotLight(i)->r *
								lightingHandler.getDotLight(i)->is *
								mathe3D.powerof(skpLambertPhL, lightingHandler.getSpecularPower());
			   intLambertPhLG = intLambertPhLG +
								lightingHandler.getDotLight(i)->g *
                                lightingHandler.getDotLight(i)->is *
								mathe3D.powerof(skpLambertPhL, lightingHandler.getSpecularPower());
               intLambertPhLB = intLambertPhLB + 
                                lightingHandler.getDotLight(i)->b *
								lightingHandler.getDotLight(i)->is *
								mathe3D.powerof(skpLambertPhL, lightingHandler.getSpecularPower());

           }           
      }
      
      if (intLambertPhLR > 255) intLambertPhLR = 255;
      if (intLambertPhLG > 255) intLambertPhLG = 255;
	  if (intLambertPhLB > 255) intLambertPhLB = 255;

	  if (intLambert > 1)
          intLambert = 1;
  }

  // preparation for PHONG shading (LAMBERT lighting and PHONG shading)
  if (libSetup.texShader == _phongTexShader_)
  {
	 nv1XPhongS = p1s->vnxtc;
     nv1YPhongS = p1s->vnytc;
	 nv1ZPhongS = p1s->vnztc;

	 nv2XPhongS = p2s->vnxtc;
	 nv2YPhongS = p2s->vnytc;
     nv2ZPhongS = p2s->vnztc;

	 nv3XPhongS = p3s->vnxtc;
	 nv3YPhongS = p3s->vnytc;
	 nv3ZPhongS = p3s->vnztc;

	 nvXP1P2PhS = nv1XPhongS;
     nvYP1P2PhS = nv1YPhongS;
     nvZP1P2PhS = nv1ZPhongS;

     nvXP1P3PhS = nv1XPhongS;
     nvYP1P3PhS = nv1YPhongS;
	 nvZP1P3PhS = nv1ZPhongS;

	 nvXP2P3PhS = nv2XPhongS;
     nvYP2P3PhS = nv2YPhongS;
	 nvZP2P3PhS = nv2ZPhongS;

	 dnvXP1P3PhS = (nv3XPhongS - nv1XPhongS) / dsyP1P3;
	 dnvYP1P3PhS = (nv3YPhongS - nv1YPhongS) / dsyP1P3;
     dnvZP1P3PhS = (nv3ZPhongS - nv1ZPhongS) / dsyP1P3;
  }
  
  // preparation for GOURAUD shading (GOURAUD shading and PHONG lighting)
  if (libSetup.texShader == _gouraudTexShader_)
  {

	  intGouraudV1 = s->ia;
	  intGouraudV2 = s->ia;
	  intGouraudV3 = s->ia;

      intGouraudV1PhLR = 0;
      intGouraudV1PhLG = 0;
	  intGouraudV1PhLB = 0;

      intGouraudV2PhLR = 0;
      intGouraudV2PhLG = 0;
	  intGouraudV2PhLB = 0;

	  intGouraudV3PhLR = 0;
      intGouraudV3PhLG = 0;
	  intGouraudV3PhLB = 0;

	  for (i=0; i<lightingHandler.getDotLightCount(); i++)
	  {
           LightRay.x = lightingHandler.getDotLight(i)->xt - p1s->xtc;
		   LightRay.y = lightingHandler.getDotLight(i)->yt - p1s->ytc;
           LightRay.z = lightingHandler.getDotLight(i)->zt - p1s->ztc;

		   LightVector = mathe3D.normalizeVector(mathe3D.doV3D(LightRay.x,LightRay.y,LightRay.z));
		   skpGouraudV1 = mathe3D.dotProduct(mathe3D.doV3D(LightVector.d[0],LightVector.d[1],LightVector.d[2]),
                                             mathe3D.doV3D(p1s->vnxtc, p1s->vnytc, p1s->vnztc));

		   if ( skpGouraudV1 < 0 ) skpGouraudV1 *= 0;

           intGouraudV1 = intGouraudV1 + lightingHandler.getDotLight(i)->id*skpGouraudV1;           
           if (intGouraudV1 > 1)
			   intGouraudV1 = 1;

		   ViewRay.x =  -cameraVector.xt - p1s->xtc;
           ViewRay.y =  -cameraVector.yt - p1s->ytc;
           ViewRay.z =  -cameraVector.zt - p1s->ztc;

		   // vlGouraud1 = mathe3D.vectorLength(mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z));

           if ( ( libSetup.texLighting == _phongLighting_ ) && (skpGouraudV1 != 0) ) {

			   ViewVector = mathe3D.normalizeVector(mathe3D.doV3D(ViewRay.x,ViewRay.y,ViewRay.z));

			   NormalVector.d[0] = p1s->vnxtc;
               NormalVector.d[1] = p1s->vnytc;
			   NormalVector.d[2] = p1s->vnztc;
               
               RVector = mathe3D.normalizeVector(
						 mathe3D.subVectors(
                         mathe3D.multSkVector(2*skpGouraudV1,NormalVector),
						 LightVector));

			   skpGouraudV1PhL = mathe3D.dotProduct(mathe3D.doV3D(ViewVector.d[0],ViewVector.d[1],ViewVector.d[2]),
													mathe3D.doV3D(RVector.d[0], RVector.d[1], RVector.d[2]));
                                                 
               if ( skpGouraudV1PhL < 0 ) skpGouraudV1PhL *= 0;

               intGouraudV1PhLR = intGouraudV1PhLR + 
                                  lightingHandler.getDotLight(i)->r * 
                                  lightingHandler.getDotLight(i)->is *
                                  mathe3D.powerof(skpGouraudV1PhL, lightingHandler.getSpecularPower());
			   intGouraudV1PhLG = intGouraudV1PhLG +
                                  lightingHandler.getDotLight(i)->g * 
								  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV1PhL, lightingHandler.getSpecularPower());
			   intGouraudV1PhLB = intGouraudV1PhLB+
								  lightingHandler.getDotLight(i)->b *
								  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV1PhL, lightingHandler.getSpecularPower());

		   }
           LightRay.x = lightingHandler.getDotLight(i)->xt - p2s->xtc;
           LightRay.y = lightingHandler.getDotLight(i)->yt - p2s->ytc;
		   LightRay.z = lightingHandler.getDotLight(i)->zt - p2s->ztc;

           LightVector = mathe3D.normalizeVector(mathe3D.doV3D(LightRay.x,LightRay.y,LightRay.z));
		   skpGouraudV2 = mathe3D.dotProduct(mathe3D.doV3D(LightVector.d[0],LightVector.d[1],LightVector.d[2]),
											 mathe3D.doV3D(p2s->vnxtc, p2s->vnytc, p2s->vnztc));

		   if ( skpGouraudV2 < 0 ) skpGouraudV2 *= 0;

           intGouraudV2 = intGouraudV2 + lightingHandler.getDotLight(i)->id*skpGouraudV2;           
           if (intGouraudV2 > 1)
               intGouraudV2 = 1;
               
           ViewRay.x =  -cameraVector.xt - p2s->xtc;
		   ViewRay.y =  -cameraVector.yt - p2s->ytc;
           ViewRay.z =  -cameraVector.zt - p2s->ztc;

		   // vlGouraud2 = mathe3D.vectorLength(mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z));

		   if ( ( libSetup.texLighting == _phongLighting_ ) && (skpGouraudV2 != 0) ) {

			   ViewVector = mathe3D.normalizeVector(mathe3D.doV3D(ViewRay.x,ViewRay.y,ViewRay.z));
           
			   NormalVector.d[0] = p2s->vnxtc;
               NormalVector.d[1] = p2s->vnytc;
               NormalVector.d[2] = p2s->vnztc;

               RVector = mathe3D.normalizeVector(
                         mathe3D.subVectors(
						 mathe3D.multSkVector(2*skpGouraudV2,NormalVector),
						 LightVector));

               skpGouraudV2PhL = mathe3D.dotProduct(mathe3D.doV3D(ViewVector.d[0],ViewVector.d[1],ViewVector.d[2]),
													mathe3D.doV3D(RVector.d[0], RVector.d[1], RVector.d[2]));
                                                 
               if ( skpGouraudV2PhL < 0 ) skpGouraudV2PhL *= 0;

               intGouraudV2PhLR = intGouraudV2PhLR +
                                  lightingHandler.getDotLight(i)->r *
								  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV2PhL, lightingHandler.getSpecularPower());
			   intGouraudV2PhLG = intGouraudV2PhLG +
                                  lightingHandler.getDotLight(i)->g * 
								  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV2PhL, lightingHandler.getSpecularPower());
			   intGouraudV2PhLB = intGouraudV2PhLB +
								  lightingHandler.getDotLight(i)->b *
                                  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV2PhL, lightingHandler.getSpecularPower());
           }
           LightRay.x = lightingHandler.getDotLight(i)->xt - p3s->xtc;
		   LightRay.y = lightingHandler.getDotLight(i)->yt - p3s->ytc;
		   LightRay.z = lightingHandler.getDotLight(i)->zt - p3s->ztc;

		   LightVector = mathe3D.normalizeVector(mathe3D.doV3D(LightRay.x,LightRay.y,LightRay.z));
		   skpGouraudV3 = mathe3D.dotProduct(mathe3D.doV3D(LightVector.d[0],LightVector.d[1],LightVector.d[2]),
											 mathe3D.doV3D(p3s->vnxtc, p3s->vnytc, p3s->vnztc));
                                         
           if ( skpGouraudV3 < 0 ) skpGouraudV3 *= 0;

           intGouraudV3 = intGouraudV3 + lightingHandler.getDotLight(i)->id*skpGouraudV3;           
           if (intGouraudV3 > 1)
               intGouraudV3 = 1;

		   ViewRay.x =  -cameraVector.xt - p3s->xtc;
           ViewRay.y =  -cameraVector.yt - p3s->ytc;
		   ViewRay.z =  -cameraVector.zt - p3s->ztc;

		   // vlGouraud3 = mathe3D.vectorLength(mathe3D.doV3D(ViewRay.x, ViewRay.y, ViewRay.z));

		   if ( ( libSetup.texLighting == _phongLighting_ ) && (skpGouraudV3 != 0) ) {

               ViewVector = mathe3D.normalizeVector(mathe3D.doV3D(ViewRay.x,ViewRay.y,ViewRay.z));

               NormalVector.d[0] = p3s->vnxtc;
               NormalVector.d[1] = p3s->vnytc;
			   NormalVector.d[2] = p3s->vnztc;

               RVector = mathe3D.normalizeVector(
						 mathe3D.subVectors(
						 mathe3D.multSkVector(2*skpGouraudV3,NormalVector),
						 LightVector));

               skpGouraudV3PhL = mathe3D.dotProduct(mathe3D.doV3D(ViewVector.d[0],ViewVector.d[1],ViewVector.d[2]),
                                                    mathe3D.doV3D(RVector.d[0], RVector.d[1], RVector.d[2]));
                                                 
               if ( skpGouraudV3PhL < 0 ) skpGouraudV3PhL *= 0;

			   intGouraudV3PhLR = intGouraudV3PhLR +
								  lightingHandler.getDotLight(i)->r *
                                  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV3PhL, lightingHandler.getSpecularPower());
               intGouraudV3PhLG = intGouraudV3PhLG + 
								  lightingHandler.getDotLight(i)->g *
								  lightingHandler.getDotLight(i)->is *
								  mathe3D.powerof(skpGouraudV3PhL, lightingHandler.getSpecularPower());
			   intGouraudV3PhLB = intGouraudV3PhLB +
                                  lightingHandler.getDotLight(i)->b * 
								  lightingHandler.getDotLight(i)->is *
                                  mathe3D.powerof(skpGouraudV3PhL, lightingHandler.getSpecularPower());
           }
	  }

	  if (intGouraudV1PhLR > 255) intGouraudV1PhLR = 255;
	  if (intGouraudV1PhLG > 255) intGouraudV1PhLG = 255;
	  if (intGouraudV1PhLB > 255) intGouraudV1PhLB = 255;

      if (intGouraudV2PhLR > 255) intGouraudV2PhLR = 255;
	  if (intGouraudV2PhLG > 255) intGouraudV2PhLG = 255;
      if (intGouraudV2PhLB > 255) intGouraudV2PhLB = 255;

      if (intGouraudV3PhLR > 255) intGouraudV3PhLR = 255;
	  if (intGouraudV3PhLG > 255) intGouraudV3PhLG = 255;
      if (intGouraudV3PhLB > 255) intGouraudV3PhLB = 255;

	  intP1P2 = intGouraudV1;
	  intP1P3 = intGouraudV1;
      intP2P3 = intGouraudV2;
	  dintP1P3 = (intGouraudV3 - intGouraudV1) / dsyP1P3;

	  intP1P2PhLR = intGouraudV1PhLR;
	  intP1P2PhLG = intGouraudV1PhLG;
	  intP1P2PhLB = intGouraudV1PhLB;

	  intP1P3PhLR = intGouraudV1PhLR;
      intP1P3PhLG = intGouraudV1PhLG;
	  intP1P3PhLB = intGouraudV1PhLB;

      intP2P3PhLR = intGouraudV2PhLR;
	  intP2P3PhLG = intGouraudV2PhLG;
	  intP2P3PhLB = intGouraudV2PhLB;

      dintP1P3PhLR = (intGouraudV3PhLR - intGouraudV1PhLR) / dsyP1P3;
      dintP1P3PhLG = (intGouraudV3PhLG - intGouraudV1PhLG) / dsyP1P3;
	  dintP1P3PhLB = (intGouraudV3PhLB - intGouraudV1PhLB) / dsyP1P3;
  }

  // Calculate the stepping for vTexture
  dvTex = dvTP1P3;

  // Now we begin to render the current face, so now we count it
  renderedTrianglesCount++;

  // Optimizing division of dsyP1P3 to multiplication with dsyP1P3inv
  dsyP1P3inv = mathe3D.fastinv(dsyP1P3);

  if( dsyP1P2 )
  {
	  // Optimizing division of dsyP1P2 to multiplication with dsyP1P2inv
	  dsyP1P2inv = mathe3D.fastinv(dsyP1P2);
	  dxP1P2 = (p2s->xtc - p1s->xtc) * dsyP1P2inv;
	  dyP1P2 = (p2s->ytc - p1s->ytc) * dsyP1P2inv;
	  dzP1P2 = (p2s->ztc - p1s->ztc) * dsyP1P2inv;

	  duTP1P2 = (uTexP2 - uTexP1) * dsyP1P2inv;
	  dvTP1P2 = (vTexP2 - vTexP1) * dsyP1P2inv;
	  dzTP1P2 = (zTexP2 - zTexP1) * dsyP1P2inv;

	  dintP1P2 = (intGouraudV2 - intGouraudV1) * dsyP1P2inv;

	  dintP1P2PhLR = (intGouraudV2PhLR - intGouraudV1PhLR) * dsyP1P2inv;
	  dintP1P2PhLG = (intGouraudV2PhLG - intGouraudV1PhLG) * dsyP1P2inv;
	  dintP1P2PhLB = (intGouraudV2PhLB - intGouraudV1PhLB) * dsyP1P2inv;

	  dnvXP1P2PhS = (nv2XPhongS - nv1XPhongS) * dsyP1P2inv;
	  dnvYP1P2PhS = (nv2YPhongS - nv1YPhongS) * dsyP1P2inv;
	  dnvZP1P2PhS = (nv2ZPhongS - nv1ZPhongS) * dsyP1P2inv;

	  for(i=p1s->sy;i<p2s->sy;i++)
	  {

		  Lx = (int) (p1s->sx   + ((i   - p1s->sy)   * dsxP1P2) * dsyP1P2inv);
		  Rx = (int) (p1s->sx   + ((i   - p1s->sy)   * dsxP1P3) * dsyP1P3inv);

		  LT.Lx = Lx;
		  LT.Rx = Rx;
		  LT.y = i;
		  LT.tex = pTex;
		  LT.dvTex = dvTex;
		  LT.intLambert = intLambert;
		  LT.intLambertPhLR = intLambertPhLR;
		  LT.intLambertPhLG = intLambertPhLG;
		  LT.intLambertPhLB = intLambertPhLB;
		  LT.Lint = intP1P2;
		  LT.Rint = intP1P3;
		  LT.LintR = intP1P2PhLR;
		  LT.LintG = intP1P2PhLG;
		  LT.LintB = intP1P2PhLB;
		  LT.RintR = intP1P3PhLR;
		  LT.RintG = intP1P3PhLG;
		  LT.RintB = intP1P3PhLB;
		  LT.LnvX = nvXP1P2PhS;
		  LT.LnvY = nvYP1P2PhS;
		  LT.LnvZ = nvZP1P2PhS;
		  LT.RnvX = nvXP1P3PhS;
          LT.RnvY = nvYP1P3PhS;
		  LT.RnvZ = nvZP1P3PhS;
		  LT.Lx3D = xP1P2;
		  LT.Ly3D = yP1P2;
		  LT.Lz3D = zP1P2;
		  LT.Rx3D = xP1P3;
		  LT.Ry3D = yP1P3;
		  LT.Rz3D = zP1P3;
		  LT.LuT3D = uTP1P2;
		  LT.LvT3D = vTP1P2;
		  LT.LzT3D = zTP1P2;
		  LT.RuT3D = uTP1P3;
		  LT.RvT3D = vTP1P3;
		  LT.RzT3D = zTP1P3;

		  if ( (Lx-Rx) != 0 )
		  {
			  if ( renderHLine(cpu, &LT) == false)
				  return false;
		  }

		  xP1P2 += dxP1P2;
		  yP1P2 += dyP1P2;
		  zP1P2 += dzP1P2;
		  xP1P3 += dxP1P3;
		  yP1P3 += dyP1P3;
		  zP1P3 += dzP1P3;

		  uTP1P2 += duTP1P2;
		  vTP1P2 += dvTP1P2;
		  zTP1P2 += dzTP1P2;
		  uTP1P3 += duTP1P3;
          vTP1P3 += dvTP1P3;
		  zTP1P3 += dzTP1P3;

		  intP1P2 += dintP1P2;
		  intP1P3 += dintP1P3;

		  if (libSetup.texShader == _gouraudTexShader_)
		  {
              intP1P2PhLR += dintP1P2PhLR;
              intP1P2PhLG += dintP1P2PhLG;
              intP1P2PhLB += dintP1P2PhLB;
			  intP1P3PhLR += dintP1P3PhLR;
              intP1P3PhLG += dintP1P3PhLG;
              intP1P3PhLB += dintP1P3PhLB;
		  }

		  if (libSetup.texShader == _phongTexShader_)
		  {
			  nvXP1P2PhS += dnvXP1P2PhS;
			  nvYP1P2PhS += dnvYP1P2PhS;
			  nvZP1P2PhS += dnvZP1P2PhS;
			  nvXP1P3PhS += dnvXP1P3PhS;
			  nvYP1P3PhS += dnvYP1P3PhS;
			  nvZP1P3PhS += dnvZP1P3PhS;
		  }
	  }

  }

  if( dsyP2P3 )
  {
	  // Optimizing division of dsyP2P3 to multiplication with dsyP2P3inv
	  dsyP2P3inv = mathe3D.fastinv(dsyP2P3);

	  dxP2P3 = (p3s->xtc - p2s->xtc) * dsyP2P3inv;
	  dyP2P3 = (p3s->ytc - p2s->ytc) * dsyP2P3inv;
	  dzP2P3 = (p3s->ztc - p2s->ztc) * dsyP2P3inv;

	  duTP2P3 = (uTexP3 - uTexP2) * dsyP2P3inv;
	  dvTP2P3 = (vTexP3 - vTexP2) * dsyP2P3inv;
	  dzTP2P3 = (zTexP3 - zTexP2) * dsyP2P3inv;

	  dintP2P3 = (intGouraudV3 - intGouraudV2) * dsyP2P3inv;

	  dintP2P3PhLR = (intGouraudV3PhLR - intGouraudV2PhLR) * dsyP2P3inv;
	  dintP2P3PhLG = (intGouraudV3PhLG - intGouraudV2PhLG) * dsyP2P3inv;
	  dintP2P3PhLB = (intGouraudV3PhLB - intGouraudV2PhLB) * dsyP2P3inv;

	  dnvXP2P3PhS = (nv3XPhongS - nv2XPhongS) * dsyP2P3inv;
	  dnvYP2P3PhS = (nv3YPhongS - nv2YPhongS) * dsyP2P3inv;
	  dnvZP2P3PhS = (nv3ZPhongS - nv2ZPhongS) * dsyP2P3inv;

	  for(i=p2s->sy;i<p3s->sy;i++)
	  {

		  Lx = (int) (p1s->sx   + ((i   - p1s->sy)   * dsxP1P3) * dsyP1P3inv);
		  Rx = (int) (p2s->sx   + ((i   - p2s->sy)   * dsxP2P3) * dsyP2P3inv);

		  LT.Lx = Lx;
		  LT.Rx = Rx;
          LT.y = i;
		  LT.tex = pTex;
          LT.dvTex = dvTex;
		  LT.intLambert = intLambert;
          LT.intLambertPhLR = intLambertPhLR;
		  LT.intLambertPhLG = intLambertPhLG;
          LT.intLambertPhLB = intLambertPhLB;
		  LT.Lint = intP1P3;
		  LT.Rint = intP2P3;
		  LT.LintR = intP1P3PhLR;
		  LT.LintG = intP1P3PhLG;
		  LT.LintB = intP1P3PhLB;
		  LT.RintR = intP2P3PhLR;
          LT.RintG = intP2P3PhLG;
		  LT.RintB = intP2P3PhLB;
          LT.LnvX = nvXP1P3PhS;
          LT.LnvY = nvYP1P3PhS;
		  LT.LnvZ = nvZP1P3PhS;
          LT.RnvX = nvXP2P3PhS;
		  LT.RnvY = nvYP2P3PhS;
          LT.RnvZ = nvZP2P3PhS;
          LT.Lx3D = xP1P3;
          LT.Ly3D = yP1P3;
		  LT.Lz3D = zP1P3;
		  LT.Rx3D = xP2P3;
		  LT.Ry3D = yP2P3;
		  LT.Rz3D = zP2P3;
		  LT.LuT3D = uTP1P3;
		  LT.LvT3D = vTP1P3;
		  LT.LzT3D = zTP1P3;
		  LT.RuT3D = uTP2P3;
		  LT.RvT3D = vTP2P3;
		  LT.RzT3D = zTP2P3;

		  if ( (Lx-Rx) != 0 )
		  {
			  if ( renderHLine(cpu, &LT) == false)
				  return false;
		  }

		  xP1P3 += dxP1P3;
		  yP1P3 += dyP1P3;
		  zP1P3 += dzP1P3;
		  xP2P3 += dxP2P3;
		  yP2P3 += dyP2P3;
		  zP2P3 += dzP2P3;

		  uTP1P3 += duTP1P3;
		  vTP1P3 += dvTP1P3;
		  zTP1P3 += dzTP1P3;
          uTP2P3 += duTP2P3;
		  vTP2P3 += dvTP2P3;
          zTP2P3 += dzTP2P3;

          intP1P3 += dintP1P3;
		  intP2P3 += dintP2P3;

		  if (libSetup.texShader == _gouraudTexShader_)
          {
			  intP1P3PhLR += dintP1P3PhLR;
              intP1P3PhLG += dintP1P3PhLG;
			  intP1P3PhLB += dintP1P3PhLB;
              intP2P3PhLR += dintP2P3PhLR;
              intP2P3PhLG += dintP2P3PhLG;
			  intP2P3PhLB += dintP2P3PhLB;
          }

		  if (libSetup.texShader == _phongTexShader_)
          {
			  nvXP1P3PhS += dnvXP1P3PhS;
              nvYP1P3PhS += dnvYP1P3PhS;
              nvZP1P3PhS += dnvZP1P3PhS;
              nvXP2P3PhS += dnvXP2P3PhS;
              nvYP2P3PhS += dnvYP2P3PhS;
			  nvZP2P3PhS += dnvZP2P3PhS;
          }
	  }

  }

  // Reset texture vertexes to 0
  md->getVert(p1)->u = 0;
  md->getVert(p1)->v = 0;
  md->getVert(p2)->u = 0;
  md->getVert(p2)->v = 0;
  md->getVert(p3)->u = 0;
  md->getVert(p3)->v = 0;

  return true;
}



///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::buildTriangleList( ... )
///////////////////////////////////////////////////////////////////////////////
// build the list of triangle included in the next rendering
///////////////////////////////////////////////////////////////////////////////
int C3DLibrary::buildTriangleList()
{
  int i, j;
  int sizeTriangleList = 0;

  TCamera *vp;
  TMeshVertex cameraVector;
  float distance;
  CMeshData *pmesh;
  TMeshTriangle *pface;

  float invthree = mathe3D.fastinv( 3.0f );

  vp = cameraHandler.getCamera(cameraHandler.getActiveCamera());

  // prepare the camera (you are sitting in the origin of the camera space)
  cameraVector.xt = vp->camOriginX;
  cameraVector.yt = vp->camOriginY;
  cameraVector.zt = vp->camOriginZ;

  for (i = 0; i < getMeshCount(); i++)
  {
	   pmesh = m_meshStorage.getMesh(i);

	   for (j = 0; j < pmesh->getFaceCount(); j++) {
			pface = pmesh->getFace(j);

			if ( ( pface->bfCulled == false ) &&
				 ( pface->zClipped == false ) )
			{
				distance = 1.0f;
#ifdef USE_PAINTERS_ALG
				p1 = pmesh->getVert(pface->v[0]);
				p2 = pmesh->getVert(pface->v[1]);
				p3 = pmesh->getVert(pface->v[2]);

				// This is slower
				// p1 = getVertex(i, getTriangle(i,j)->v[0]);
				// p2 = getVertex(i, getTriangle(i,j)->v[1]);
				// p3 = getVertex(i, getTriangle(i,j)->v[2]);

				distanceVector.d[0] = -cameraVector.xt - ( ( p1->xtc + p2->xtc + p3->xtc) * invthree );
				distanceVector.d[1] = -cameraVector.yt - ( ( p1->ytc + p2->ytc + p3->ytc) * invthree );
				distanceVector.d[2] = -cameraVector.zt - ( ( p1->ztc + p2->ztc + p3->ztc) * invthree );

				// This is slower
				// distanceVector.d[0] = -cameraVector.xt - ( ( p1->xtc + p2->xtc + p3->xtc) / 3 );
				// distanceVector.d[1] = -cameraVector.yt - ( ( p1->ytc + p2->ytc + p3->ytc) / 3 );
				// distanceVector.d[2] = -cameraVector.zt - ( ( p1->ztc + p2->ztc + p3->ztc) / 3  );

				// Using the dotProduct means saving the slow sqrt
				distance = mathe3D.dotProduct(distanceVector, distanceVector);
#endif
				// Saving the corresponding mesh number
				zOrderTrList[sizeTriangleList].mesh = i;
				// Saving a pointer to the triangle
				zOrderTrList[sizeTriangleList].t = pface;
				// Selecting the clostest distance
				zOrderTrList[sizeTriangleList].t->distance = distance;
				sizeTriangleList++;
				if (sizeTriangleList >= _sizeOfTriangleList_)
					return -1;
			}
		}
  }

  return sizeTriangleList;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::quickSortTriangleList( ... )
///////////////////////////////////////////////////////////////////////////////
// sorts the triangle list zOrderTrList for painting from the back to the front
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::quickSortTriangleList( int iLo, int iHi )
{
  int Lo, Hi;
  float Mid;

  TOrderedTrList tmpTr;

#ifndef USE_PAINTERS_ALG
  return true;
#endif

  Lo = iLo;
  Hi = iHi;
  Mid = zOrderTrList[(Lo + Hi) / 2].t->distance;

  while (Lo <= Hi) {

	  while ( zOrderTrList[Lo].t->distance < Mid ) { Lo++; };
	  while ( zOrderTrList[Hi].t->distance > Mid ) { Hi--; };
      
	  if (Lo <= Hi)
      {
          tmpTr = zOrderTrList[Lo];
		  zOrderTrList[Lo] = zOrderTrList[Hi];
		  zOrderTrList[Hi] = tmpTr;

		  Lo++;
          Hi--;
      }
      
  }
 
  if (Hi > iLo) quickSortTriangleList( iLo, Hi);
  if (Lo < iHi) quickSortTriangleList( Lo, iHi);

  return true;
}

#ifdef __MULTI_CORE_SUPPORT
///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::setThreadState( ... )
///////////////////////////////////////////////////////////////////////////////
// Set all worker thread state
///////////////////////////////////////////////////////////////////////////////
void C3DLibrary::setThreadState(unsigned char workerThreadState) {
  memset(&threadState[0], workerThreadState, numberWorkerThreads);
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::checkThreadState( ... )
///////////////////////////////////////////////////////////////////////////////
// Check if all worker threads have a certain state
///////////////////////////////////////////////////////////////////////////////
unsigned char C3DLibrary::checkThreadState(unsigned char workerThreadState)
{
  unsigned char i;
  unsigned char stateShared = 0;

  for (i=0; i<numberWorkerThreads; i++)
    if (threadState[i] == workerThreadState)
      stateShared++;

  return stateShared;
}

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::workerThread( ... )
///////////////////////////////////////////////////////////////////////////////
// Worker thread for multi core support
///////////////////////////////////////////////////////////////////////////////
unsigned long C3DLibrary::workerThread(unsigned char nrThreadParams, unsigned char *threadParams) 
{
  int            i;
  unsigned int   x, y, bufferPos;
  unsigned char  cpu, minZCPU;
  float          minZ, compZ;
  unsigned int   sizeX, sizeY, singleChunkY, myStartChunk, myEndChunk;
  unsigned long  j, singleFaceChunk, myStartFaceChunk, myEndFaceChunk;
  TZData        *myZBufferPtrs[__CPU_CORES_SUPPORTED];
  T2DData       *myScreenPtrs[__CPU_CORES_SUPPORTED];

  unsigned char  threadID = threadParams[0];
  unsigned char  threads = threadParams[1];

  numberWorkerThreads = threads;

  // This worker thread is ready to run
  threadState[threadID] = __THREAD_STATE_READY;

  while (1) {
	if (threadState[threadID] == __THREAD_STATE_PROCESS_REQUEST) {
	  // One worker thread started
      threadState[threadID] = __THREAD_STATE_PROCESSING;

      for (i = 0; i < getMeshCount(); i++)
      {
		if (i % threads == threadID) {
	      if ( libSetup.texRenderer == _texRenderer_ )
		    if ( transformNormals(i) == false )
			   return false;
	      if ( (m_meshStorage.getMesh(i)->get3DPosition()->skal_changed)||
	 		 (m_meshStorage.getMesh(i)->get3DPosition()->rot_changed) ||
	 		 (m_meshStorage.getMesh(i)->get3DPosition()->trans_changed) )
		  	   if ( complgeometricops(i) == false )
			     return false;

	      if ( transformCamera(i) == false )
	        return false;
	      if ( zClipping(i) == false )
	        return false;
	      if ( backFaceCulling(i) == false )
	        return false;
	      if ( projection(i) == false )
	   	    return false;

	      clearPositionMap(i);
	    }
      }

	  // One worker thread finished
      threadState[threadID] = __THREAD_STATE_FINISHED;
	} 
	else if (threadState[threadID] == __THREAD_STATE_RENDER_REQUEST) {
	  // One worker thread started
      threadState[threadID] = __THREAD_STATE_RENDERING;

	  // Calculate the chunk of the data set to be rendered by this worker
      singleFaceChunk = (sizeTriangleList / threads);
	  if (threadID == threads - 1) {
	    myStartFaceChunk = singleFaceChunk * threadID;
	    myEndFaceChunk = sizeTriangleList;
	  } else {
	    myStartFaceChunk = singleFaceChunk * threadID;
		myEndFaceChunk = singleFaceChunk * threadID + singleFaceChunk;
	  }

	  // Render data chunk
	  for (j = myStartFaceChunk; j < myEndFaceChunk; j++)
	  {
 	      if ( renderTriangle(threadID, j) == false )
		    return false;
      }

	  // One worker thread finished
      threadState[threadID] = __THREAD_STATE_FINISHED;
	} else if (threadState[threadID] == __THREAD_STATE_JOIN_REQUEST) {
	  // One worker thread started
      threadState[threadID] = __THREAD_STATE_JOINING;

	  sizeY = screen2D.getSizeY();
      sizeX = screen2D.getSizeX();

	  // Calculate the chunk of the data set to be joined by this worker
      singleChunkY = (sizeY / threads) + 1;
	  if (threadID == threads - 1) {
	    myStartChunk = singleChunkY * threadID;
	    myEndChunk = sizeY;
	  } else {
	    myStartChunk = singleChunkY * threadID;
		myEndChunk = singleChunkY * threadID + singleChunkY;
	  }

	  // Get fast (direct) access to memory buffers
	  for (i=0; i<threads; i++) {
        myZBufferPtrs[i] = zBufferCPU[i]->getBufferHandle();
	    myScreenPtrs[i] = screen2DCPU[i]->getScreenHandle();
	  }

	  // Join data sets (having a minimum of data frontiers)
	  for(y = myStartChunk; y < myEndChunk; y++) {
        for (x = 0; x < sizeX; x++) {
		  bufferPos = y*sizeX+x;
	  	  minZCPU = 0;
		  minZ = myZBufferPtrs[minZCPU]->d[bufferPos];
	      for (cpu = 1; cpu < threads; cpu++) {
  		    compZ = myZBufferPtrs[cpu]->d[bufferPos];
			if (compZ < minZ) {
	   	      minZCPU = cpu;
		      minZ = compZ;
		    }
		  }
		  if (minZ != _zBufferClear_) {
		    screen2D.getScreenHandle()->d[bufferPos] = myScreenPtrs[minZCPU]->d[bufferPos];
		    // zBuffer.getBufferHandle()->d[bufferPos] = minZ;
		  }
		}
	  }

	  // One worker thread finished
      threadState[threadID] = __THREAD_STATE_FINISHED;
	} else {
#ifdef _WINDOWS
	  Sleep(1);
#endif
	}
  }

  return 0;
}
#endif

///////////////////////////////////////////////////////////////////////////////
// C3DLibrary::renderScene( ... )
///////////////////////////////////////////////////////////////////////////////
// Library main function: renders the whole scene depending on libSetup
///////////////////////////////////////////////////////////////////////////////
bool C3DLibrary::renderScene()
{
  int i, j;
  unsigned int v0,v1,v2;

#ifdef __MULTI_CORE_SUPPORT
  // Any workers available?
  if (numberWorkerThreads == 0)
    return true;

  // All workers available?
  if (checkThreadState(__THREAD_STATE_READY) != numberWorkerThreads)
    return true;

  // Start multi core rendering
  setThreadState(__THREAD_STATE_PROCESS_REQUEST);

  // Wait up to the moment all workers have finished
  while (checkThreadState(__THREAD_STATE_FINISHED) != numberWorkerThreads) {
#ifdef _WINDOWS
    Sleep(1);
#endif
  }

  setThreadState(__THREAD_STATE_READY);
#else
  for (i = 0; i < getMeshCount(); i++)
  {
	   if ( libSetup.texRenderer == _texRenderer_ )
		   if ( transformNormals(i) == false )
			   return false;
	   if ( (m_meshStorage.getMesh(i)->get3DPosition()->skal_changed)||
			(m_meshStorage.getMesh(i)->get3DPosition()->rot_changed) ||
			(m_meshStorage.getMesh(i)->get3DPosition()->trans_changed) )
				if ( complgeometricops(i) == false )
				   return false;

	   if ( transformCamera(i) == false )
		   return false;
	   if ( zClipping(i) == false )
		   return false;
	   if ( backFaceCulling(i) == false )
		   return false;
	   if ( projection(i) == false )
	   	   return false;

	   clearPositionMap(i);
  }
#endif

  sizeTriangleList = buildTriangleList();
  if (sizeTriangleList == -1)
	  return false;

  // We now calculate the rendered faces in renderTriangle()
  // renderedTrianglesCount = sizeTriangleList;
  renderedTrianglesCount = 0;
  renderedPixelsCount = 0;
  renderedHLinesCount = 0;

  if ( sizeTriangleList <= 0 )
	  return true;

  quickSortTriangleList( 0, sizeTriangleList-1 );

  if ( libSetup.texRenderer == _texRenderer_ )
  {

	  if ( transformLights() == false )
	  	  return false;

#ifdef __MULTI_CORE_SUPPORT
      // Start multi core rendering
      setThreadState(__THREAD_STATE_RENDER_REQUEST);

      // Wait up to the moment all workers have finished
      while (checkThreadState(__THREAD_STATE_FINISHED) != numberWorkerThreads) {
#ifdef _WINDOWS
        Sleep(1);
#endif
      }

      setThreadState(__THREAD_STATE_READY);

      // Start joining the data set
      setThreadState(__THREAD_STATE_JOIN_REQUEST);

      // Wait up to the moment all workers have finished
      while (checkThreadState(__THREAD_STATE_FINISHED) != numberWorkerThreads) {
#ifdef _WINDOWS
        Sleep(1);
#endif
      }

      setThreadState(__THREAD_STATE_READY);
#else
	  for (i = 0; i < sizeTriangleList; i++)
	  {

		   if ( renderTriangle(0, i) == false )
			   return false;

      }
#endif

      // Apply the Full Screen Anti Aliasing
	  if ( libSetup.fsAA == _bilinearFSAntiAliasing_ )
		   screen2D.bilinearScrFilter();
	  if ( libSetup.fsAA == _bilinear2XFSAntiAliasing_ )
	  {
		   screen2D.bilinearScrFilter();
		   screen2D.bilinearScrFilter();
	  }
	  if ( libSetup.fsAA == _bilinear4XFSAntiAliasing_ )
	  {
		   screen2D.bilinearScrFilter();
		   screen2D.bilinearScrFilter();
	  }
  }

  if ( libSetup.texRenderer == _wireFrameRenderer_ )
  {

	  for (i = 0; i < getMeshCount(); i++)
	  {

		   for (j = 0; j < getMesh(i)->getFaceCount(); j++)
		   {

			  if ( m_meshStorage.getMesh(i)->getFace(j)->zClipped == false )
			  {

				  v0 = getMesh(i)->getFace(j)->v[0];
				  v1 = getMesh(i)->getFace(j)->v[1];
                  v2 = getMesh(i)->getFace(j)->v[2];
                
                  renderLine(getMesh(i)->getVert(v0)->sx,
							 getMesh(i)->getVert(v0)->sy,
                             getMesh(i)->getVert(v1)->sx,
                             getMesh(i)->getVert(v1)->sy,
                             libSetup.wireFrameColor);

                  renderLine(getMesh(i)->getVert(v1)->sx,
							 getMesh(i)->getVert(v1)->sy,
                             getMesh(i)->getVert(v2)->sx,
							 getMesh(i)->getVert(v2)->sy,
							 libSetup.wireFrameColor);

				  renderLine(getMesh(i)->getVert(v2)->sx,
							 getMesh(i)->getVert(v2)->sy,
							 getMesh(i)->getVert(v0)->sx,
							 getMesh(i)->getVert(v0)->sy,
							 libSetup.wireFrameColor);

			  }

		   }
	   }
  }

  if (libSetup.showLogo)
	  screen2D.drawPicture(screen2D.getSizeX()-logo.sizeX-10, screen2D.getSizeY()-logo.sizeY-10, &logo);

  return true;
}